2

I have a silly problem with response headers only showing in 304 responses. The server I am using is Tomcat 7. The java filter contains the following code:

HttpServletResponse httpResponse = (HttpServletResponse) response;

httpResponse.addHeader("Access-Control-Allow-Origin", "*");
httpResponse.addHeader("Access-Control-Allow-Credentials", "true");
httpResponse.addHeader("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
httpResponse.addHeader("Access-Control-Allow-Headers", "Content-Type, Accept");

if (debug) {
    httpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1
    httpResponse.setHeader("Pragma", "no-cache"); // HTTP 1.0
    httpResponse.setDateHeader("Expires", 0); // Proxies.
} else {
    httpResponse.setHeader("Cache-Control", "public");
    httpResponse.setDateHeader("Expires", System.currentTimeMillis() + 86400000L); // 1Day 
}

It is important to note that this filter is the last to fire in the chain, it is the first filter to be mapped in the web.xml (makes no difference if it is the last either) and the above code runs after

chain.doFilter(request, response);

Using Chrome's audit tool to evaluate the headers I notice that on a 200 the headers are not in the response. I refresh the page, by pressing "F5", which leads to a 304 and the headers are there to be seen. Likewise I refresh again and get a 200 where the headers are not to be seen. Repeatedly refreshing the page alternates between 304 and 200 responses and on each 304 the headers are there, yet not on the 200.

The filter is mapped to all resources.

<filter-mapping>
    <filter-name>AllowRemoteAccessHeaderFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>HttpCachingFilter</filter-name>
    <url-pattern>*</url-pattern>
</filter-mapping>

Additionally the filter fires on each refresh, irrespective of the response.

Can someone explain to me what is actually happening here? I am sure that I am missing something small. My goal is to have the headers there for a 200 response.

EDIT: Noticed that this works for the first page loaded, but not any of the resources that are loaded on that page, eg. scripts, images, css includes.

EDIT: Noticed as well that if I remove all scriptlets from the page then the headers come through. The two scriptlets each include data that was set in the request object via another filter. An example of the one that injects the compiled javascript is below:

<script type="text/javascript"><%= request.getAttribute("compiled-scripts")%></script>

PROBLEM: I might have found the cause of the problem, but not a solution. When I include the compiled scripts into the page the transfer encoding changes from having the content length set to:

Transfer-Encoding:chunked

The moment the transfer encoding is chunked the headers do not come through. This might explain why on a 304 the response headers come through, because the content length is known and therefore set. Where as on a 200 the content length is unknown and the transfer encoding chunked.

However, my problem still remains. I want to include the compiled scripts instead of referencing them to reduce latency and other reasons. I have read that to prevent transfer encoding chunked you need to set the content length manually, by reading the response in as bytes. I am not aware of how I can determine the response length in a filter.

Is it possible to have the response headers come through when the transfer encoding is chunked?

avanderw
  • 675
  • 1
  • 7
  • 22

2 Answers2

0

If the goal is to have a 200 response, you can try this :

httpResponse.setStatus(HttpServletResponse.SC_OK)

But a 304 response is not a bad response.

Pignic
  • 499
  • 3
  • 12
0

I found the answer eventually. Thanks to a hint in this post: Why do I need to modify buffer and autoflush attributes in a JSP?

More specifically this

When the buffer is flushed once, redirection or forwarding won't work because all changes to the HTTP response header must occur the first time a buffer is sent to the client.

The response header was being set after the chain.doFilter(), which was causing the problem. By setting the headers before the call to chain.doFilter() the issue was sorted. Meaning that the response headers were coming through on all requests and response codes.

My understanding is that when the buffer is full, as in this case, it is flushed. This causes the chunked response. However, at the time the buffer is flushed the headers are not set. The browser receives the first chunk minus the response headers and only checks the headers on the first chunk. Thus by setting the response headers before the buffer is flushed, the browser receives the headers with the first chunk.

Community
  • 1
  • 1
avanderw
  • 675
  • 1
  • 7
  • 22