Skip to content
accelerando

Tag Archives: sendfile

Apache httpd, Tomcat und sendfile

28-Jun-11

I used to use mod_xsendfile by Nils Maier, who’s Homepage doesn’t seem to exist anymore, to send files from Ruby proxied by Apache respectively powered by modrails. Those files shouldn’t be in any public www directory as authorization needs to be checked, but are accessed very often so that streaming them is not an option.

To use this technique you need mod_xsendfile, which is attached to this post.

I just have rewritten my application from Ruby on Rails to Java and it’s easy to add the necessary headers in a HttpServletResponse:

response.setHeader("X-Sendfile", file.getAbsolutePath());
response.flushBuffer();

You may add other headers like Content-Type and the like but you must not modify the body, hence the flushBuffer.

This works quite well… As long as had my Apache httpd running with mpm-prefork.

Switching to Apache mpm-worker caused some problems. I cannot say with a final conclusion that mod_xsendfile was causing troubles but i started to see the wrong files (images in this case) delivered or not delivered at all.

My alternate solution was streaming the files using Channels from java.nio but CPU usage went nuts.

The solution now employed is the using Apache Tomcats asynchronous writes that are available since Tomcat 6. Their documentation is rather short but so is implementing them:

/*
HttpServletRequest request = ...
HttpServletResponse response = ...
*/
 
if(request != null && Boolean.TRUE.equals(request.getAttribute("org.apache.tomcat.sendfile.support"))) {		
  long l = file.length();
  request.setAttribute("org.apache.tomcat.sendfile.filename", absolutePath);
  request.setAttribute("org.apache.tomcat.sendfile.start", 0l);
  request.setAttribute("org.apache.tomcat.sendfile.end", l);
  response.setHeader("Content-Length", Long.toString(l));
  response.flushBuffer();
} else if(use_xsendfile) {
  // see above
} else {
  // stream files
}

What i got wrong at first was setting those attributes in the response. That didn’t work. The must be set in the request and you must take care setting all of those and with the correct type (String respectively long). And that’s it.

The request attribute org.apache.tomcat.sendfile.support will be true when the connector is configured to either use the APR connector (org.apache.coyote.http11.Http11AprProtocol) or the non blocking Java connector (org.apache.coyote.http11.Http11NioProtocol) (the later one being easier to deploy as it has no external dependencies).

CPU usage for sending those files is now nearly 0.

Close
E-mail It