Example usage for javax.servlet.http HttpServletResponse SC_PARTIAL_CONTENT

List of usage examples for javax.servlet.http HttpServletResponse SC_PARTIAL_CONTENT

Introduction

In this page you can find the example usage for javax.servlet.http HttpServletResponse SC_PARTIAL_CONTENT.

Prototype

int SC_PARTIAL_CONTENT

To view the source code for javax.servlet.http HttpServletResponse SC_PARTIAL_CONTENT.

Click Source Link

Document

Status code (206) indicating that the server has fulfilled the partial GET request for the resource.

Usage

From source file:com.enonic.cms.framework.util.HttpServletRangeUtilTest.java

@Test
public void test_process_request_plain_some_range() throws Exception {
    final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest();
    httpServletRequest.setMethod("GET");
    httpServletRequest.setPathInfo("/input.dat");
    httpServletRequest.addHeader(HttpHeaders.RANGE, "bytes=-48");

    final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
    HttpServletRangeUtil.processRequest(httpServletRequest, mockHttpServletResponse, "input.dat",
            "application/pdf", INPUT_FILE, false);

    assertEquals("CcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz",
            mockHttpServletResponse.getContentAsString());

    assertEquals(HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus());
    assertEquals("application/pdf", mockHttpServletResponse.getContentType());
    assertEquals("inline;filename=\"input.dat\"",
            mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_DISPOSITION));
    assertEquals("48", mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_LENGTH));

}

From source file:com.enonic.cms.framework.util.HttpServletRangeUtilTest.java

@Test
public void test_process_request_plain_range_minus() throws Exception {
    final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest();
    httpServletRequest.setMethod("GET");
    httpServletRequest.setPathInfo("/input.dat");
    httpServletRequest.addHeader(HttpHeaders.RANGE, "bytes=50-");

    final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
    HttpServletRangeUtil.processRequest(httpServletRequest, mockHttpServletResponse, "input.dat",
            "application/pdf", INPUT_FILE, false);

    assertEquals("Zz", mockHttpServletResponse.getContentAsString());

    assertEquals(HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus());
    assertEquals("application/pdf", mockHttpServletResponse.getContentType());
    assertEquals("inline;filename=\"input.dat\"",
            mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_DISPOSITION));
    assertEquals("2", mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_LENGTH));

}

From source file:org.nuxeo.ecm.core.io.download.DownloadServiceImpl.java

@Override
public void downloadBlob(HttpServletRequest request, HttpServletResponse response, DocumentModel doc,
        String xpath, Blob blob, String filename, String reason, Map<String, Serializable> extendedInfos,
        Boolean inline, Consumer<ByteRange> blobTransferer) throws IOException {
    Objects.requireNonNull(blob);
    // check blob permissions
    if (!checkPermission(doc, xpath, blob, reason, extendedInfos)) {
        response.sendError(HttpServletResponse.SC_FORBIDDEN, "Permission denied");
        return;/*from w  w w.j  av  a 2s .  c  o m*/
    }

    // check Blob Manager download link
    URI uri = redirectResolver.getURI(blob, UsageHint.DOWNLOAD, request);
    if (uri != null) {
        try {
            Map<String, Serializable> ei = new HashMap<>();
            if (extendedInfos != null) {
                ei.putAll(extendedInfos);
            }
            ei.put("redirect", uri.toString());
            logDownload(doc, xpath, filename, reason, ei);
            response.sendRedirect(uri.toString());
        } catch (IOException ioe) {
            DownloadHelper.handleClientDisconnect(ioe);
        }
        return;
    }

    try {
        String digest = blob.getDigest();
        if (digest == null) {
            digest = DigestUtils.md5Hex(blob.getStream());
        }
        String etag = '"' + digest + '"'; // with quotes per RFC7232 2.3
        response.setHeader("ETag", etag); // re-send even on SC_NOT_MODIFIED
        addCacheControlHeaders(request, response);

        String ifNoneMatch = request.getHeader("If-None-Match");
        if (ifNoneMatch != null) {
            boolean match = false;
            if (ifNoneMatch.equals("*")) {
                match = true;
            } else {
                for (String previousEtag : StringUtils.split(ifNoneMatch, ", ")) {
                    if (previousEtag.equals(etag)) {
                        match = true;
                        break;
                    }
                }
            }
            if (match) {
                String method = request.getMethod();
                if (method.equals("GET") || method.equals("HEAD")) {
                    response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
                } else {
                    // per RFC7232 3.2
                    response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
                }
                return;
            }
        }

        // regular processing

        if (StringUtils.isBlank(filename)) {
            filename = StringUtils.defaultIfBlank(blob.getFilename(), "file");
        }
        String contentDisposition = DownloadHelper.getRFC2231ContentDisposition(request, filename, inline);
        response.setHeader("Content-Disposition", contentDisposition);
        response.setContentType(blob.getMimeType());
        if (blob.getEncoding() != null) {
            response.setCharacterEncoding(blob.getEncoding());
        }

        long length = blob.getLength();
        response.setHeader("Accept-Ranges", "bytes");
        String range = request.getHeader("Range");
        ByteRange byteRange;
        if (StringUtils.isBlank(range)) {
            byteRange = null;
        } else {
            byteRange = DownloadHelper.parseRange(range, length);
            if (byteRange == null) {
                log.error("Invalid byte range received: " + range);
            } else {
                response.setHeader("Content-Range",
                        "bytes " + byteRange.getStart() + "-" + byteRange.getEnd() + "/" + length);
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            }
        }
        long contentLength = byteRange == null ? length : byteRange.getLength();
        if (contentLength < Integer.MAX_VALUE) {
            response.setContentLength((int) contentLength);
        }

        logDownload(doc, xpath, filename, reason, extendedInfos);

        // execute the final download
        blobTransferer.accept(byteRange);
    } catch (UncheckedIOException e) {
        DownloadHelper.handleClientDisconnect(e.getCause());
    } catch (IOException ioe) {
        DownloadHelper.handleClientDisconnect(ioe);
    }
}

From source file:eionet.eunis.servlets.DownloadServlet.java

/**
 * Process the actual request./*  w w  w . ja va2s.c  om*/
 *
 * @param request The request to be processed.
 * @param response The response to be created.
 * @param content Whether the request body should be written (GET) or not (HEAD).
 * @throws IOException If something fails at I/O level.
 * @throws ServletException
 */
private void processRequest(HttpServletRequest request, HttpServletResponse response, boolean content)
        throws IOException, ServletException {

    String requestURI = request.getRequestURI();
    String contextPath = request.getContextPath();
    String pathInfo = request.getPathInfo();
    String servletPath = request.getServletPath();

    // Create the abstract file reference to the requested file.
    File file = null;
    String fileRelativePath = StringUtils.substringAfter(request.getRequestURI(), request.getContextPath());
    fileRelativePath = StringUtils.replace(fileRelativePath, "%20", " ");
    if (StringUtils.isNotEmpty(fileRelativePath) && StringUtils.isNotEmpty(appHome)) {
        file = new File(appHome, fileRelativePath);
    }

    // If file was not found, send 404.
    if (file == null || !file.exists() || file.isDirectory()) {
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }

    // Prepare some variables. The ETag is an unique identifier of the file.
    String fileName = file.getName();
    long length = file.length();
    long lastModified = file.lastModified();
    String eTag = fileName + "_" + length + "_" + lastModified;

    // Validate request headers for caching ---------------------------------------------------

    // If-None-Match header should contain "*" or ETag. If so, then return 304.
    String ifNoneMatch = request.getHeader("If-None-Match");
    if (ifNoneMatch != null && matches(ifNoneMatch, eTag)) {
        response.setHeader("ETag", eTag); // Required in 304.
        response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
        return;
    }

    // If-Modified-Since header should be greater than LastModified. If so, then return 304.
    // This header is ignored if any If-None-Match header is specified.
    long ifModifiedSince = request.getDateHeader("If-Modified-Since");
    if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) {
        response.setHeader("ETag", eTag); // Required in 304.
        response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
        return;
    }

    // Validate request headers for resume ----------------------------------------------------

    // If-Match header should contain "*" or ETag. If not, then return 412.
    String ifMatch = request.getHeader("If-Match");
    if (ifMatch != null && !matches(ifMatch, eTag)) {
        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
        return;
    }

    // If-Unmodified-Since header should be greater than LastModified. If not, then return 412.
    long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since");
    if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) {
        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
        return;
    }

    // Validate and process range -------------------------------------------------------------

    // Prepare some variables. The full Range represents the complete file.
    Range full = new Range(0, length - 1, length);
    List<Range> ranges = new ArrayList<Range>();

    // Validate and process Range and If-Range headers.
    String range = request.getHeader("Range");
    if (range != null) {

        // Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416.
        if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) {
            response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
            response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            return;
        }

        // If-Range header should either match ETag or be greater then LastModified. If not,
        // then return full file.
        String ifRange = request.getHeader("If-Range");
        if (ifRange != null && !ifRange.equals(eTag)) {
            try {
                long ifRangeTime = request.getDateHeader("If-Range"); // Throws IAE if invalid.
                if (ifRangeTime != -1 && ifRangeTime + 1000 < lastModified) {
                    ranges.add(full);
                }
            } catch (IllegalArgumentException ignore) {
                ranges.add(full);
            }
        }

        // If any valid If-Range header, then process each part of byte range.
        if (ranges.isEmpty()) {
            for (String part : range.substring(6).split(",")) {
                // Assuming a file with length of 100, the following examples returns bytes at:
                // 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100).
                long start = sublong(part, 0, part.indexOf("-"));
                long end = sublong(part, part.indexOf("-") + 1, part.length());

                if (start == -1) {
                    start = length - end;
                    end = length - 1;
                } else if (end == -1 || end > length - 1) {
                    end = length - 1;
                }

                // Check if Range is syntactically valid. If not, then return 416.
                if (start > end) {
                    response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
                    response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                    return;
                }

                // Add range.
                ranges.add(new Range(start, end, length));
            }
        }
    }

    // Prepare and initialize response --------------------------------------------------------

    // Get content type by file name and set default GZIP support and content disposition.
    String contentType = getServletContext().getMimeType(fileName);
    boolean acceptsGzip = false;
    String disposition = "inline";

    // If content type is unknown, then set the default value.
    // For all content types, see: http://www.w3schools.com/media/media_mimeref.asp
    // To add new content types, add new mime-mapping entry in web.xml.
    if (contentType == null) {
        contentType = "application/octet-stream";
    }

    // If content type is text, then determine whether GZIP content encoding is supported by
    // the browser and expand content type with the one and right character encoding.
    // Else, expect for images, determine content disposition. If content type is supported by
    // the browser, then set to inline, else attachment which will pop a 'save as' dialogue.
    if (contentType.startsWith("text")) {
        String acceptEncoding = request.getHeader("Accept-Encoding");
        acceptsGzip = acceptEncoding != null && accepts(acceptEncoding, "gzip");
        contentType += ";charset=UTF-8";
    } else if (!contentType.startsWith("image")) {
        String accept = request.getHeader("Accept");
        disposition = accept != null && accepts(accept, contentType) ? "inline" : "attachment";
    }

    // Initialize response.
    response.reset();
    response.setBufferSize(DEFAULT_BUFFER_SIZE);
    response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\"");
    response.setHeader("Accept-Ranges", "bytes");
    response.setHeader("ETag", eTag);
    response.setDateHeader("Last-Modified", lastModified);
    response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME);

    // Send requested file (part(s)) to client ------------------------------------------------

    // Prepare streams.
    RandomAccessFile input = null;
    OutputStream output = null;

    try {
        // Open streams.
        input = new RandomAccessFile(file, "r");
        output = response.getOutputStream();

        if (ranges.isEmpty() || ranges.get(0) == full) {

            // Return full file.
            Range r = full;
            response.setContentType(contentType);
            response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);

            if (content) {
                if (acceptsGzip) {
                    // The browser accepts GZIP, so GZIP the content.
                    response.setHeader("Content-Encoding", "gzip");
                    output = new GZIPOutputStream(output, DEFAULT_BUFFER_SIZE);
                } else {
                    // Content length is not directly predictable in case of GZIP.
                    // So only add it if there is no means of GZIP, else browser will hang.
                    response.setHeader("Content-Length", String.valueOf(r.length));
                }

                // Copy full range.
                copy(input, output, r.start, r.length);
            }

        } else if (ranges.size() == 1) {

            // Return single part of file.
            Range r = ranges.get(0);
            response.setContentType(contentType);
            response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);
            response.setHeader("Content-Length", String.valueOf(r.length));
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.

            if (content) {
                // Copy single part range.
                copy(input, output, r.start, r.length);
            }

        } else {

            // Return multiple parts of file.
            response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY);
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.

            if (content) {
                // Cast back to ServletOutputStream to get the easy println methods.
                ServletOutputStream sos = (ServletOutputStream) output;

                // Copy multi part range.
                for (Range r : ranges) {
                    // Add multipart boundary and header fields for every range.
                    sos.println();
                    sos.println("--" + MULTIPART_BOUNDARY);
                    sos.println("Content-Type: " + contentType);
                    sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total);

                    // Copy single part range of multi part range.
                    copy(input, output, r.start, r.length);
                }

                // End with multipart boundary.
                sos.println();
                sos.println("--" + MULTIPART_BOUNDARY + "--");
            }
        }
    } finally {
        // Gently close streams.
        close(output);
        close(input);
    }
}

From source file:eionet.gdem.web.FileDownloadServlet.java

/**
 * Process the actual request.//from w  w  w. jav a  2s.  c  o m
 *
 * @param request
 *            The request to be processed.
 * @param response
 *            The response to be created.
 * @param content
 *            Whether the request body should be written (GET) or not (HEAD).
 * @throws IOException
 *             If something fails at I/O level.
 * @throws ServletException If an error occurs.
 */
private void processRequest(HttpServletRequest request, HttpServletResponse response, boolean content)
        throws IOException, ServletException {

    String urlPath = URLDecoder
            .decode(StringUtils.substringAfter(request.getRequestURI(), request.getContextPath()), "UTF-8");
    String filePath = Properties.appRootFolder + urlPath;

    String securityMessage = checkPermissions(request, urlPath);
    if (securityMessage != null) {
        handleNotAuthorised(securityMessage, request, response);
        return;
    }

    // Get the file object from the file store
    File file = new File(filePath);

    // If file was not found, send 404.
    if (file == null || !file.exists() || !file.isFile()) {
        handleFileNotFound("Could not find file by the following URI: " + request.getRequestURI(), request,
                response);
        return;
    }

    // Prepare some variables. The ETag is an unique identifier of the file.
    String fileName = file.getName();
    long length = file.length();
    long lastModified = file.lastModified();
    String eTag = fileName + "_" + length + "_" + lastModified;

    // Validate request headers for caching ---------------------------------------------------

    // If-None-Match header should contain "*" or ETag. If so, then return 304.
    String ifNoneMatch = request.getHeader("If-None-Match");
    if (ifNoneMatch != null && matches(ifNoneMatch, eTag)) {
        response.setHeader("ETag", eTag); // Required in 304.
        response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
        return;
    }

    // If-Modified-Since header should be greater than LastModified. If so, then return 304.
    // This header is ignored if any If-None-Match header is specified.
    long ifModifiedSince = request.getDateHeader("If-Modified-Since");
    if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) {
        response.setHeader("ETag", eTag); // Required in 304.
        response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
        return;
    }

    // Validate request headers for resume ----------------------------------------------------

    // If-Match header should contain "*" or ETag. If not, then return 412.
    String ifMatch = request.getHeader("If-Match");
    if (ifMatch != null && !matches(ifMatch, eTag)) {
        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
        return;
    }

    // If-Unmodified-Since header should be greater than LastModified. If not, then return 412.
    long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since");
    if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) {
        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
        return;
    }

    // Validate and process range -------------------------------------------------------------

    // Prepare some variables. The full Range represents the complete file.
    Range full = new Range(0, length - 1, length);
    List<Range> ranges = new ArrayList<Range>();

    // Validate and process Range and If-Range headers.
    String range = request.getHeader("Range");
    if (range != null) {

        // Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416.
        if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) {
            response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
            response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            return;
        }

        // If-Range header should either match ETag or be greater then LastModified. If not,
        // then return full file.
        String ifRange = request.getHeader("If-Range");
        if (ifRange != null && !ifRange.equals(eTag)) {
            try {
                long ifRangeTime = request.getDateHeader("If-Range"); // Throws IAE if invalid.
                if (ifRangeTime != -1 && ifRangeTime + 1000 < lastModified) {
                    ranges.add(full);
                }
            } catch (IllegalArgumentException ignore) {
                ranges.add(full);
            }
        }

        // If any valid If-Range header, then process each part of byte range.
        if (ranges.isEmpty()) {
            for (String part : range.substring(6).split(",")) {
                // Assuming a file with length of 100, the following examples returns bytes at:
                // 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100).
                long start = sublong(part, 0, part.indexOf("-"));
                long end = sublong(part, part.indexOf("-") + 1, part.length());

                if (start == -1) {
                    start = length - end;
                    end = length - 1;
                } else if (end == -1 || end > length - 1) {
                    end = length - 1;
                }

                // Check if Range is syntactically valid. If not, then return 416.
                if (start > end) {
                    response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
                    response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                    return;
                }

                // Add range.
                ranges.add(new Range(start, end, length));
            }
        }
    }

    // Prepare and initialize response --------------------------------------------------------

    // Get content type by file name and set default GZIP support and content disposition.
    String contentType = getServletContext().getMimeType(fileName);
    boolean acceptsGzip = false;
    String disposition = "inline";

    // If content type is unknown, then set the default value.
    // For all content types, see: http://www.w3schools.com/media/media_mimeref.asp
    // To add new content types, add new mime-mapping entry in web.xml.
    if (contentType == null) {
        contentType = "text/plain";
    }

    // If content type is text, then determine whether GZIP content encoding is supported by
    // the browser and expand content type with the one and right character encoding.
    // Else, expect for images, determine content disposition. If content type is supported by
    // the browser, then set to inline, else attachment which will pop a 'save as' dialogue.
    if (contentType.startsWith("text")) {
        String acceptEncoding = request.getHeader("Accept-Encoding");
        acceptsGzip = acceptEncoding != null && accepts(acceptEncoding, "gzip");
        contentType += ";charset=UTF-8";
    } else if (!contentType.startsWith("image")) {
        String accept = request.getHeader("Accept");
        disposition = accept != null && accepts(accept, contentType) ? "inline" : "attachment";
    }

    // Initialize response.
    response.reset();
    response.setBufferSize(DEFAULT_BUFFER_SIZE);
    response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\"");
    response.setHeader("Accept-Ranges", "bytes");
    response.setHeader("ETag", eTag);
    response.setDateHeader("Last-Modified", lastModified);
    response.setDateHeader("Expires", System.currentTimeMillis() + DEFAULT_EXPIRE_TIME);

    // Send requested file (part(s)) to client ------------------------------------------------

    // Prepare streams.
    RandomAccessFile input = null;
    OutputStream output = null;

    try {
        // Open streams.
        input = new RandomAccessFile(file, "r");
        output = response.getOutputStream();

        if (ranges.isEmpty() || ranges.get(0) == full) {

            // Return full file.
            Range r = full;
            response.setContentType(contentType);
            response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);

            if (content) {
                if (acceptsGzip) {
                    // The browser accepts GZIP, so GZIP the content.
                    response.setHeader("Content-Encoding", "gzip");
                    output = new GZIPOutputStream(output, DEFAULT_BUFFER_SIZE);
                } else {
                    // Content length is not directly predictable in case of GZIP.
                    // So only add it if there is no means of GZIP, else browser will hang.
                    response.setHeader("Content-Length", String.valueOf(r.length));
                }

                // Copy full range.
                copy(input, output, r.start, r.length);
            }

        } else if (ranges.size() == 1) {

            // Return single part of file.
            Range r = ranges.get(0);
            response.setContentType(contentType);
            response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);
            response.setHeader("Content-Length", String.valueOf(r.length));
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.

            if (content) {
                // Copy single part range.
                copy(input, output, r.start, r.length);
            }

        } else {

            // Return multiple parts of file.
            response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY);
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.

            if (content) {
                // Cast back to ServletOutputStream to get the easy println methods.
                ServletOutputStream sos = (ServletOutputStream) output;

                // Copy multi part range.
                for (Range r : ranges) {
                    // Add multipart boundary and header fields for every range.
                    sos.println();
                    sos.println("--" + MULTIPART_BOUNDARY);
                    sos.println("Content-Type: " + contentType);
                    sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total);

                    // Copy single part range of multi part range.
                    copy(input, output, r.start, r.length);
                }

                // End with multipart boundary.
                sos.println();
                sos.println("--" + MULTIPART_BOUNDARY + "--");
            }
        }
    } finally {
        // Gently close streams.
        close(output);
        close(input);
    }
}

From source file:com.enonic.cms.framework.util.HttpServletRangeUtilTest.java

@Test
public void test_process_request_plain_last_range_minus() throws Exception {
    final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest();
    httpServletRequest.setMethod("GET");
    httpServletRequest.setPathInfo("/input.dat");
    httpServletRequest.addHeader(HttpHeaders.RANGE, "bytes=51-");

    final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse();
    HttpServletRangeUtil.processRequest(httpServletRequest, mockHttpServletResponse, "input.dat",
            "application/pdf", INPUT_FILE, false);

    assertEquals("z", mockHttpServletResponse.getContentAsString());

    assertEquals(HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus());
    assertEquals("application/pdf", mockHttpServletResponse.getContentType());
    assertEquals("inline;filename=\"input.dat\"",
            mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_DISPOSITION));
    assertEquals("1", mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_LENGTH));

}

From source file:org.openmrs.module.patientnarratives.web.servlet.FileServlet.java

/**
 * Process the actual request./*from   w w  w.  j a v a 2  s  .  c o m*/
 * @param request The request to be processed.
 * @param response The response to be created.
 * @param content Whether the request body should be written (GET) or not (HEAD).
 * @throws IOException If something fails at I/O level.
 */
private void processRequest(HttpServletRequest request, HttpServletResponse response, boolean content)
        throws IOException {
    // Validate the requested file ------------------------------------------------------------

    Integer videoObsId = Integer.parseInt(request.getParameter("obsId"));
    Obs complexObs = Context.getObsService().getComplexObs(videoObsId, OpenmrsConstants.RAW_VIEW);
    ComplexData complexData = complexObs.getComplexData();
    byte[] videoObjectData = ((byte[]) complexData.getData());

    String fileExt_file = complexData.getTitle();
    String arrFileExt[] = fileExt_file.split(" ");

    tempMergedVideoFile = createFile(arrFileExt[0]);
    String requestedFile = tempMergedVideoFile.getCanonicalPath();

    FileUtils.writeByteArrayToFile(new File(requestedFile), videoObjectData);

    // Check if file is actually supplied to the request URL.
    if (requestedFile == null) {
        // Do your thing if the file is not supplied to the request URL.
        // Throw an exception, or send 404, or show default/warning page, or just ignore it.
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }

    // URL-decode the file name (might contain spaces and on) and prepare file object.
    File file = new File(requestedFile);

    // Check if file actually exists in filesystem.
    if (!file.exists()) {
        // Do your thing if the file appears to be non-existing.
        // Throw an exception, or send 404, or show default/warning page, or just ignore it.
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }

    // Prepare some variables. The ETag is an unique identifier of the file.
    String fileName = file.getName();
    long length = file.length();
    long lastModified = file.lastModified();
    String eTag = fileName + "_" + length + "_" + lastModified;
    long expires = System.currentTimeMillis() + DEFAULT_EXPIRE_TIME;

    // Validate request headers for caching ---------------------------------------------------

    // If-None-Match header should contain "*" or ETag. If so, then return 304.
    String ifNoneMatch = request.getHeader("If-None-Match");
    if (ifNoneMatch != null && matches(ifNoneMatch, eTag)) {
        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
        response.setHeader("ETag", eTag); // Required in 304.
        response.setDateHeader("Expires", expires); // Postpone cache with 1 week.
        return;
    }

    // If-Modified-Since header should be greater than LastModified. If so, then return 304.
    // This header is ignored if any If-None-Match header is specified.
    long ifModifiedSince = request.getDateHeader("If-Modified-Since");
    if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > lastModified) {
        response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
        response.setHeader("ETag", eTag); // Required in 304.
        response.setDateHeader("Expires", expires); // Postpone cache with 1 week.
        return;
    }

    // Validate request headers for resume ----------------------------------------------------

    // If-Match header should contain "*" or ETag. If not, then return 412.
    String ifMatch = request.getHeader("If-Match");
    if (ifMatch != null && !matches(ifMatch, eTag)) {
        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
        return;
    }

    // If-Unmodified-Since header should be greater than LastModified. If not, then return 412.
    long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since");
    if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= lastModified) {
        response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED);
        return;
    }

    // Validate and process range -------------------------------------------------------------

    // Prepare some variables. The full Range represents the complete file.
    Range full = new Range(0, length - 1, length);
    List<Range> ranges = new ArrayList<Range>();

    // Validate and process Range and If-Range headers.
    String range = request.getHeader("Range");
    if (range != null) {

        // Range header should match format "bytes=n-n,n-n,n-n...". If not, then return 416.
        if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) {
            response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
            response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            return;
        }

        // If-Range header should either match ETag or be greater then LastModified. If not,
        // then return full file.
        String ifRange = request.getHeader("If-Range");
        if (ifRange != null && !ifRange.equals(eTag)) {
            try {
                long ifRangeTime = request.getDateHeader("If-Range"); // Throws IAE if invalid.
                if (ifRangeTime != -1 && ifRangeTime + 1000 < lastModified) {
                    ranges.add(full);
                }
            } catch (IllegalArgumentException ignore) {
                ranges.add(full);
            }
        }

        // If any valid If-Range header, then process each part of byte range.
        if (ranges.isEmpty()) {
            for (String part : range.substring(6).split(",")) {
                // Assuming a file with length of 100, the following examples returns bytes at:
                // 50-80 (50 to 80), 40- (40 to length=100), -20 (length-20=80 to length=100).
                long start = sublong(part, 0, part.indexOf("-"));
                long end = sublong(part, part.indexOf("-") + 1, part.length());

                if (start == -1) {
                    start = length - end;
                    end = length - 1;
                } else if (end == -1 || end > length - 1) {
                    end = length - 1;
                }

                // Check if Range is syntactically valid. If not, then return 416.
                if (start > end) {
                    response.setHeader("Content-Range", "bytes */" + length); // Required in 416.
                    response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                    return;
                }

                // Add range.
                ranges.add(new Range(start, end, length));
            }
        }
    }

    // Prepare and initialize response --------------------------------------------------------

    // Get content type by file name and set default GZIP support and content disposition.
    String contentType = getServletContext().getMimeType(fileName);
    boolean acceptsGzip = false;
    String disposition = "inline";

    // If content type is unknown, then set the default value.
    // For all content types, see: http://www.w3schools.com/media/media_mimeref.asp
    // To add new content types, add new mime-mapping entry in web.xml.
    if (contentType == null) {
        contentType = "application/octet-stream";
    }

    // If content type is text, then determine whether GZIP content encoding is supported by
    // the browser and expand content type with the one and right character encoding.
    if (contentType.startsWith("text")) {
        String acceptEncoding = request.getHeader("Accept-Encoding");
        acceptsGzip = acceptEncoding != null && accepts(acceptEncoding, "gzip");
        contentType += ";charset=UTF-8";
    }

    // Else, expect for images, determine content disposition. If content type is supported by
    // the browser, then set to inline, else attachment which will pop a 'save as' dialogue.
    else if (!contentType.startsWith("image")) {
        String accept = request.getHeader("Accept");
        disposition = accept != null && accepts(accept, contentType) ? "inline" : "attachment";
    }

    // Initialize response.
    response.reset();
    response.setBufferSize(DEFAULT_BUFFER_SIZE);
    response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\"");
    response.setHeader("Accept-Ranges", "bytes");
    response.setHeader("ETag", eTag);
    response.setDateHeader("Last-Modified", lastModified);
    response.setDateHeader("Expires", expires);

    // Send requested file (part(s)) to client ------------------------------------------------

    // Prepare streams.
    RandomAccessFile input = null;
    OutputStream output = null;

    try {
        // Open streams.
        input = new RandomAccessFile(file, "r");
        output = response.getOutputStream();

        if (ranges.isEmpty() || ranges.get(0) == full) {

            // Return full file.
            Range r = full;
            response.setContentType(contentType);
            response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);

            if (content) {
                if (acceptsGzip) {
                    // The browser accepts GZIP, so GZIP the content.
                    response.setHeader("Content-Encoding", "gzip");
                    output = new GZIPOutputStream(output, DEFAULT_BUFFER_SIZE);
                } else {
                    // Content length is not directly predictable in case of GZIP.
                    // So only add it if there is no means of GZIP, else browser will hang.
                    response.setHeader("Content-Length", String.valueOf(r.length));
                }

                // Copy full range.
                copy(input, output, r.start, r.length);
            }

        } else if (ranges.size() == 1) {

            // Return single part of file.
            Range r = ranges.get(0);
            response.setContentType(contentType);
            response.setHeader("Content-Range", "bytes " + r.start + "-" + r.end + "/" + r.total);
            response.setHeader("Content-Length", String.valueOf(r.length));
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.

            if (content) {
                // Copy single part range.
                copy(input, output, r.start, r.length);
            }

        } else {

            // Return multiple parts of file.
            response.setContentType("multipart/byteranges; boundary=" + MULTIPART_BOUNDARY);
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206.

            if (content) {
                // Cast back to ServletOutputStream to get the easy println methods.
                ServletOutputStream sos = (ServletOutputStream) output;

                // Copy multi part range.
                for (Range r : ranges) {
                    // Add multipart boundary and header fields for every range.
                    sos.println();
                    sos.println("--" + MULTIPART_BOUNDARY);
                    sos.println("Content-Type: " + contentType);
                    sos.println("Content-Range: bytes " + r.start + "-" + r.end + "/" + r.total);

                    // Copy single part range of multi part range.
                    copy(input, output, r.start, r.length);
                }

                // End with multipart boundary.
                sos.println();
                sos.println("--" + MULTIPART_BOUNDARY + "--");
            }
        }
    } finally {
        // Gently close streams.
        close(output);
        close(input);
        tempMergedVideoFile.delete();
    }
}

From source file:com.xpn.xwiki.web.DownloadActionTest.java

@Test
public void testValidStartRange() throws XWikiException, IOException {
    // This test expects bytes 0, 1, 2 and 3 from the file.
    final Date d = new Date();
    createAttachment(d, DEFAULT_FILE_NAME);
    setRequestExpectations(DEFAULT_URI, null, null, "bytes=0-3", -1l);
    setResponseExpectations(d.getTime(), 4);
    setOutputExpectations(0, 4);/*from ww w .  j  a  va 2 s . com*/
    getMockery().checking(new Expectations() {
        {
            one(DownloadActionTest.this.response).setStatus(with(HttpServletResponse.SC_PARTIAL_CONTENT));
            one(DownloadActionTest.this.response).setHeader(with("Content-Range"),
                    with("bytes 0-3/" + DownloadActionTest.this.fileContent.length));
        }
    });
    Assert.assertNull(this.action.render(getContext()));
}

From source file:org.alfresco.repo.web.util.HttpRangeProcessor.java

/**
 * Process multiple ranges.//from   ww w  .  j  av a  2  s.c  o  m
 * 
 * @param res        HttpServletResponse
 * @param range      Range header value
 * @param ref        NodeRef to the content for streaming
 * @param property   Content Property for the content
 * @param mimetype   Mimetype of the content
 * @param userAgent  User Agent of the caller
 * 
 * @return true if processed range, false otherwise
 */
private boolean processMultiRange(Object res, String range, NodeRef ref, QName property, String mimetype,
        String userAgent) throws IOException {
    final Log logger = getLogger();

    // Handle either HttpServletResponse or WebScriptResponse
    HttpServletResponse httpServletResponse = null;
    WebScriptResponse webScriptResponse = null;
    if (res instanceof HttpServletResponse) {
        httpServletResponse = (HttpServletResponse) res;
    } else if (res instanceof WebScriptResponse) {
        webScriptResponse = (WebScriptResponse) res;
    }
    if (httpServletResponse == null && webScriptResponse == null) {
        // Unknown response object type
        return false;
    }

    // return the sets of bytes as requested in the content-range header
    // the response will be formatted as multipart/byteranges media type message

    /* Examples of byte-ranges-specifier values (assuming an entity-body of length 10000):
            
    - The first 500 bytes (byte offsets 0-499, inclusive):  bytes=0-499
    - The second 500 bytes (byte offsets 500-999, inclusive):
      bytes=500-999
    - The final 500 bytes (byte offsets 9500-9999, inclusive):
      bytes=-500
    - Or bytes=9500-
    - The first and last bytes only (bytes 0 and 9999):  bytes=0-0,-1
    - Several legal but not canonical specifications of byte offsets 500-999, inclusive:
       bytes=500-600,601-999
       bytes=500-700,601-999 */

    boolean processedRange = false;

    // get the content reader
    ContentReader reader = contentService.getReader(ref, property);

    final List<Range> ranges = new ArrayList<Range>(8);
    long entityLength = reader.getSize();
    for (StringTokenizer t = new StringTokenizer(range, ", "); t.hasMoreTokens(); /**/) {
        try {
            ranges.add(Range.constructRange(t.nextToken(), mimetype, entityLength));
        } catch (IllegalArgumentException err) {
            if (getLogger().isDebugEnabled())
                getLogger()
                        .debug("Failed to parse range header - returning 416 status code: " + err.getMessage());

            if (httpServletResponse != null) {
                httpServletResponse.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                httpServletResponse.setHeader(HEADER_CONTENT_RANGE, "\"*\"");
                httpServletResponse.getOutputStream().close();
            } else if (webScriptResponse != null) {
                webScriptResponse.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
                webScriptResponse.setHeader(HEADER_CONTENT_RANGE, "\"*\"");
                webScriptResponse.getOutputStream().close();
            }
            return true;
        }
    }

    if (ranges.size() != 0) {
        // merge byte ranges if possible - IE handles this well, FireFox not so much
        if (userAgent == null || userAgent.indexOf("MSIE ") != -1) {
            Collections.sort(ranges);

            for (int i = 0; i < ranges.size() - 1; i++) {
                Range first = ranges.get(i);
                Range second = ranges.get(i + 1);
                if (first.end + 1 >= second.start) {
                    if (logger.isDebugEnabled())
                        logger.debug("Merging byte range: " + first + " with " + second);

                    if (first.end < second.end) {
                        // merge second range into first
                        first.end = second.end;
                    }
                    // else we simply discard the second range - it is contained within the first

                    // delete second range
                    ranges.remove(i + 1);
                    // reset loop index
                    i--;
                }
            }
        }

        // calculate response content length
        long length = MULTIPART_BYTERANGES_BOUNDRY_END.length() + 2;
        for (Range r : ranges) {
            length += r.getLength();
        }

        // output headers as we have at least one range to process
        OutputStream os = null;
        if (httpServletResponse != null) {
            httpServletResponse.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            httpServletResponse.setHeader(HEADER_CONTENT_TYPE, MULTIPART_BYTERANGES_HEADER);
            httpServletResponse.setHeader(HEADER_CONTENT_LENGTH, Long.toString(length));
            os = httpServletResponse.getOutputStream();
        } else if (webScriptResponse != null) {
            webScriptResponse.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            webScriptResponse.setHeader(HEADER_CONTENT_TYPE, MULTIPART_BYTERANGES_HEADER);
            webScriptResponse.setHeader(HEADER_CONTENT_LENGTH, Long.toString(length));
            os = webScriptResponse.getOutputStream();
        }

        InputStream is = null;
        try {
            for (Range r : ranges) {
                if (logger.isDebugEnabled())
                    logger.debug("Processing: " + r.getContentRange());

                try {
                    // output the header bytes for the range
                    if (os instanceof ServletOutputStream)
                        r.outputHeader((ServletOutputStream) os);

                    // output the binary data for the range
                    // need a new reader for each new InputStream
                    is = contentService.getReader(ref, property).getContentInputStream();
                    streamRangeBytes(r, is, os, 0L);
                    is.close();
                    is = null;

                    // section marker and flush stream
                    if (os instanceof ServletOutputStream)
                        ((ServletOutputStream) os).println();
                    os.flush();
                } catch (IOException err) {
                    if (getLogger().isDebugEnabled())
                        getLogger().debug(
                                "Unable to process multiple range due to IO Exception: " + err.getMessage());
                    throw err;
                }
            }
        } finally {
            if (is != null) {
                is.close();
            }
        }

        // end marker
        if (os instanceof ServletOutputStream)
            ((ServletOutputStream) os).println(MULTIPART_BYTERANGES_BOUNDRY_END);
        os.close();
        processedRange = true;
    }

    return processedRange;
}

From source file:com.xpn.xwiki.web.DownloadActionTest.java

@Test
public void testValidMiddleRange() throws XWikiException, IOException {
    // This test expects bytes 3, 4 and 5 from the file.
    final Date d = new Date();
    createAttachment(d, DEFAULT_FILE_NAME);
    setRequestExpectations(DEFAULT_URI, null, null, "bytes=3-5", -1l);
    setResponseExpectations(d.getTime(), 3);
    setOutputExpectations(3, 6);/*from  w w w  . j  a v a2s . co m*/
    getMockery().checking(new Expectations() {
        {
            one(DownloadActionTest.this.response).setStatus(with(HttpServletResponse.SC_PARTIAL_CONTENT));
            one(DownloadActionTest.this.response).setHeader(with("Content-Range"),
                    with("bytes 3-5/" + DownloadActionTest.this.fileContent.length));
        }
    });
    Assert.assertNull(this.action.render(getContext()));
}