Apache httpd, Tomcat und sendfile

June 28, 2011 by Michael

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.

No comments yet

Post a Comment

Your email is never published. We need your name and email address only for verifying a legitimate comment. For more information, a copy of your saved data or a request to delete any data under this address, please send a short notice to michael@simons.ac from the address you used to comment on this entry.
By entering and submitting a comment, wether with or without name or email address, you'll agree that all data you have entered including your IP address will be checked and stored for a limited time by Automattic Inc., 60 29th Street #343, San Francisco, CA 94110-4929, USA. only for the purpose of avoiding spam. You can deny further storage of your data by sending an email to support@wordpress.com, with subject “Deletion of Data stored by Akismet”.
Required fields are marked *