Monthly Archives: October 2010

Wait, where’s my PDF?

I was recently working on a project where we needed to stream and render PDF documents from a Java servlet. Sounds easy and straight-forward, right? When initially developed, all browsers were rendering in the development environment (non-SSL). Here is the code snippet for how we were initially rendering the PDFs:

Standard Headers

  private void setHttpResponseHeaders(HttpServletRequest _request, HttpServletResponse _response, int _contentLength) {
    _response.setContentType("application/pdf");
    _response.setContentLength(_contentLength);

    _response.setHeader("Cache-Control", "no-cache");
    _response.addHeader("Cache-Control", "no-store");
    _response.addHeader("Cache-Control", "private");
    _response.addHeader("Cache-Control", "must-revalidate");
    _response.addHeader("Cache-Control", "maxage=1");

    _response.setHeader("Pragma", "no-cache");
    _response.addHeader("Pragma", "private");

    _response.setHeader("Content-Disposition", "inline; filename=form.pdf");
  }

Once we deployed to our test environment (SSL), we started to run into issues with Internet Explorer failing to render the documents. Apparently, this is a common issue. One of my co-workers originally tackled the issue by adding the “Accept-Ranges” header and the issue with IE was resolved. However, Firefox started to have issues over SSL with an error “File does not begin with %PDF-”. After much head-scratching over issues with different browsers and their behavior via SSL and non-SSL connections, the following is the final code that seems to solve the issue for most browsers:

Final

  private void setHttpResponseHeaders(HttpServletRequest _request, HttpServletResponse _response, int _contentLength) {
    _response.setContentType("application/pdf");
    _response.setContentLength(_contentLength);

    _response.setHeader("Cache-Control", "no-cache");
    _response.addHeader("Cache-Control", "no-store");
    _response.addHeader("Cache-Control", "private");
    _response.addHeader("Cache-Control", "must-revalidate");
    _response.addHeader("Cache-Control", "maxage=1");

    _response.setHeader("Pragma", "no-cache");
    _response.addHeader("Pragma", "private");

    // only add the "accept-ranges" header if the "user-agent" header indicates Internet Explorer
    String userAgent = _request.getHeader("User-Agent");
    if (userAgent != null && userAgent.indexOf("MSIE") > -1) {
      _response.setHeader("Accept-Ranges", "bytes");
    }

    _response.setHeader("Content-Disposition", "inline; filename=form.pdf");
  }

The following is a matrix of our findings:

Browser SSL non-SSL
Internet Explorer requires “Accept-Ranges” header standard headers
Firefox forbids “Accept-Ranges” header standard headers
Google Chrome forbids “Accept-Ranges” header standard headers
Safari PC standard headers standard headers