List of usage examples for javax.servlet.http HttpServletResponse SC_PARTIAL_CONTENT
int SC_PARTIAL_CONTENT
To view the source code for javax.servlet.http HttpServletResponse SC_PARTIAL_CONTENT.
Click Source Link
From source file:com.enonic.cms.framework.util.HttpServletRangeUtilTest.java
@Test public void test_gzip_ranges() throws Exception { final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); httpServletRequest.setMethod("GET"); httpServletRequest.setPathInfo("/input.js"); httpServletRequest.addHeader(HttpHeaders.RANGE, "bytes=0-0,-1"); httpServletRequest.addHeader(HttpHeaders.ACCEPT_ENCODING, "gzip"); final MockHttpServletResponse mockHttpServletResponse = new MockHttpServletResponse(); HttpServletRangeUtil.processRequest(httpServletRequest, mockHttpServletResponse, "input.js", "application/javascript", INPUT_FILE, false); assertEquals(readFromFile("response4.dat"), mockHttpServletResponse.getContentAsString()); assertEquals(HttpServletResponse.SC_PARTIAL_CONTENT, mockHttpServletResponse.getStatus()); assertEquals("multipart/byteranges; boundary=THIS_STRING_SEPARATES", mockHttpServletResponse.getContentType()); assertEquals("inline;filename=\"input.js\"", mockHttpServletResponse.getHeader(HttpHeaders.CONTENT_DISPOSITION)); }
From source file:httpUtils.HttpUtils.java
/** * Parse the request headers, build the response, stream back file * @param request//from w w w . ja v a 2 s . co m * @param response * @param dataStream * @param fileLength * @param fileName * @param fileLastModified * @param contentType * @return */ private static HttpServletResponse getFileAsStream(HttpServletRequest request, HttpServletResponse response, InputStream dataStream, long fileLength, String fileName, long fileLastModified, String contentType) { if (dataStream == null) { response.setStatus(HttpServletResponse.SC_NOT_FOUND); return response; } if (StringUtils.isEmpty(fileName) || fileLastModified == 0L) { response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); return response; } String ifNoneMatch = request.getHeader("If-None-Match"); if (ifNoneMatch != null && matches(ifNoneMatch, fileName)) { response.setHeader("ETag", fileName); response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return response; } long ifModifiedSince = request.getDateHeader("If-Modified-Since"); if (ifNoneMatch == null && ifModifiedSince != -1 && ifModifiedSince + 1000 > fileLastModified) { response.setHeader("ETag", fileName); response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return response; } String ifMatch = request.getHeader("If-Match"); if (ifMatch != null && !matches(ifMatch, fileName)) { response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED); return response; } long ifUnmodifiedSince = request.getDateHeader("If-Unmodified-Since"); if (ifUnmodifiedSince != -1 && ifUnmodifiedSince + 1000 <= fileLastModified) { response.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED); return response; } Range full = new Range(0, fileLength - 1, fileLength); List<Range> ranges = new ArrayList<Range>(); String range = request.getHeader("Range"); if (range != null) { if (!range.matches("^bytes=\\d*-\\d*(,\\d*-\\d*)*$")) { response.setHeader("Content-Range", "bytes */" + fileLength); response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return response; } String ifRange = request.getHeader("If-Range"); if (ifRange != null && !ifRange.equals(fileName)) { try { long ifRangeTime = request.getDateHeader("If-Range"); if (ifRangeTime != -1) { ranges.add(full); } } catch (IllegalArgumentException ignore) { ranges.add(full); } } 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 = fileLength - end; end = fileLength - 1; } else if (end == -1 || end > fileLength - 1) { end = fileLength - 1; } // Check if Range is syntactically valid. If not, then // return 416. if (start > end) { response.setHeader("Content-Range", "bytes */" + fileLength); // Required // in // 416. response.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return response; } // Add range. ranges.add(new Range(start, end, fileLength)); } } } // Get content type by file name and set content disposition. 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"; } else if (!contentType.startsWith("image")) { // 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. String accept = request.getHeader("Accept"); disposition = accept != null && accepts(accept, contentType) ? "inline" : "attachment"; } // Initialize response. response.reset(); response.setBufferSize(HttpUtils.DEFAULT_BUFFER_SIZE); response.setHeader("Content-Disposition", disposition + ";filename=\"" + fileName + "\""); response.setHeader("Accept-Ranges", "bytes"); response.setHeader("ETag", fileName); response.setDateHeader("Last-Modified", fileLastModified); response.setDateHeader("Expires", System.currentTimeMillis() + HttpUtils.DEFAULT_EXPIRE_TIME); // Send requested file (part(s)) to client // ------------------------------------------------ // Prepare streams. InputStream input = null; OutputStream output = null; try { // Open streams. input = new BufferedInputStream(dataStream); 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); response.setHeader("Content-Length", String.valueOf(r.length)); copy(input, output, fileLength, 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. // Copy single part range. copy(input, output, fileLength, r.start, r.length); } else { // Return multiple parts of file. response.setContentType("multipart/byteranges; boundary=" + HttpUtils.MULTIPART_BOUNDARY); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 206. // 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("--" + HttpUtils.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, fileLength, r.start, r.length); } // End with multipart boundary. sos.println(); sos.println("--" + HttpUtils.MULTIPART_BOUNDARY + "--"); } } catch (Exception e) { log.error("get file as stream failed", e); } finally { // Gently close streams. close(output); close(input); close(dataStream); } return response; }
From source file:com.xpn.xwiki.web.DownloadActionTest.java
@Test public void testValidEndRange() throws XWikiException, IOException { // This test expects bytes 9, 10, 11, 12 and 13 from the file. final Date d = new Date(); createAttachment(d, DEFAULT_FILE_NAME); setRequestExpectations(DEFAULT_URI, null, null, "bytes=9-13", -1l); setResponseExpectations(d.getTime(), this.fileContent.length - 9); setOutputExpectations(9, this.fileContent.length); getMockery().checking(new Expectations() { {/*from w ww. ja v a2s . com*/ one(DownloadActionTest.this.response).setStatus(with(HttpServletResponse.SC_PARTIAL_CONTENT)); one(DownloadActionTest.this.response).setHeader(with("Content-Range"), with("bytes 9-13/" + DownloadActionTest.this.fileContent.length)); } }); Assert.assertNull(this.action.render(getContext())); }
From source file:org.orderofthebee.addons.support.tools.share.AbstractLogFileWebScript.java
protected boolean processSingleRange(final WebScriptResponse res, final File file, final String range, final String mimetype) throws IOException { // return the specific set of bytes as requested in the content-range header /*//from w w w . java 2 s . c o m * Examples of byte-content-range-spec values, assuming that the entity contains total of 1234 bytes: * The first 500 bytes: * bytes 0-499/1234 * The second 500 bytes: * bytes 500-999/1234 * All except for the first 500 bytes: * bytes 500-1233/1234 */ /* * 'Range' header example: * bytes=10485760-20971519 */ boolean processedRange = false; Range r = null; try { r = Range.constructRange(range, mimetype, file.length()); } catch (final IllegalArgumentException err) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Failed to parse range header - returning 416 status code: " + err.getMessage()); } res.setStatus(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); res.setHeader(HEADER_CONTENT_RANGE, "\"*\""); res.getOutputStream().close(); return true; } // set Partial Content status and range headers final String contentRange = "bytes " + Long.toString(r.start) + "-" + Long.toString(r.end) + "/" + Long.toString(file.length()); res.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); res.setContentType(mimetype); res.setHeader(HEADER_CONTENT_RANGE, contentRange); res.setHeader(HEADER_CONTENT_LENGTH, Long.toString((r.end - r.start) + 1L)); if (LOGGER.isDebugEnabled()) { LOGGER.debug("Processing: Content-Range: " + contentRange); } InputStream is = null; try { // output the binary data for the range OutputStream os = null; os = res.getOutputStream(); is = new FileInputStream(file); this.streamRangeBytes(r, is, os, 0L); os.close(); processedRange = true; } catch (final IOException err) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Unable to process single range due to IO Exception: " + err.getMessage()); } throw err; } finally { if (is != null) { is.close(); } } return processedRange; }
From source file:ch.entwine.weblounge.common.impl.request.Http11ProtocolHandler.java
/** * Method generateHeaders./*ww w . ja v a 2s . c o m*/ * * @param resp * @param type */ public static void generateHeaders(HttpServletResponse resp, Http11ResponseType type) { /* generate headers only once! */ if (type.headers) return; type.headers = true; /* adjust the statistics */ ++stats[STATS_HEADER_GENERATED]; incResponseStats(type.type, headerStats); /* set the date header */ resp.setDateHeader(HEADER_DATE, type.time); /* check expires */ if (type.expires > type.time + MS_PER_YEAR) { type.expires = type.time + MS_PER_YEAR; log.warn("Expiration date too far in the future. Adjusting."); } /* set the standard headers and status code */ switch (type.type) { case RESPONSE_PARTIAL_CONTENT: if (type.expires > type.time) resp.setDateHeader(HEADER_EXPIRES, type.expires); if (type.modified > 0) { resp.setHeader(HEADER_ETAG, Http11Utils.calcETag(type.modified)); resp.setDateHeader(HEADER_LAST_MODIFIED, type.modified); } if (type.size < 0 || type.from < 0 || type.to < 0 || type.from > type.to || type.to > type.size) { resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); break; } resp.setContentLength((int) type.size); resp.setHeader(HEADER_CONTENT_RANGE, "bytes " + type.from + "-" + type.to + "/" + type.size); resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); break; case RESPONSE_OK: if (type.expires > type.time) { resp.setDateHeader(HEADER_EXPIRES, type.expires); } else if (type.expires == 0) { resp.setHeader(HEADER_CACHE_CONTROL, "no-cache"); resp.setHeader(HEADER_PRAGMA, "no-cache"); resp.setDateHeader(HEADER_EXPIRES, 0); } if (type.modified > 0) { resp.setHeader(HEADER_ETAG, Http11Utils.calcETag(type.modified)); resp.setDateHeader(HEADER_LAST_MODIFIED, type.modified); } if (type.size >= 0) resp.setContentLength((int) type.size); resp.setStatus(HttpServletResponse.SC_OK); break; case RESPONSE_NOT_MODIFIED: if (type.expires > type.time) resp.setDateHeader(HEADER_EXPIRES, type.expires); if (type.modified > 0) resp.setHeader(HEADER_ETAG, Http11Utils.calcETag(type.modified)); resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED); break; case RESPONSE_METHOD_NOT_ALLOWED: resp.setHeader(HEADER_ALLOW, "GET, POST, HEAD"); resp.setStatus(HttpServletResponse.SC_METHOD_NOT_ALLOWED); break; case RESPONSE_PRECONDITION_FAILED: if (type.expires > type.time) resp.setDateHeader(HEADER_EXPIRES, type.expires); if (type.modified > 0) { resp.setHeader(HEADER_ETAG, Http11Utils.calcETag(type.modified)); resp.setDateHeader(HEADER_LAST_MODIFIED, type.modified); } resp.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED); break; case RESPONSE_REQUESTED_RANGE_NOT_SATISFIABLE: if (type.expires > type.time) resp.setDateHeader(HEADER_EXPIRES, type.expires); if (type.modified > 0) { resp.setHeader(HEADER_ETAG, Http11Utils.calcETag(type.modified)); resp.setDateHeader(HEADER_LAST_MODIFIED, type.modified); } if (type.size >= 0) resp.setHeader(HEADER_CONTENT_RANGE, "*/" + type.size); break; case RESPONSE_INTERNAL_SERVER_ERROR: default: resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); } }
From source file:org.messic.server.facade.controllers.rest.SongController.java
@ApiMethod(path = "/services/songs/{songSid}/audio", verb = ApiVerb.GET, description = "Get the audio binary from a song resource of an album", produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE }) @ApiErrors(apierrors = { @ApiError(code = UnknownMessicRESTException.VALUE, description = "Unknown error"), @ApiError(code = NotAuthorizedMessicRESTException.VALUE, description = "Forbidden access"), @ApiError(code = IOMessicRESTException.VALUE, description = "IO error trying to get the audio resource") }) @RequestMapping(value = "/{songSid}/audio", method = { RequestMethod.GET, RequestMethod.HEAD }) @ResponseStatus(HttpStatus.OK)/*from ww w .j a v a 2s . com*/ public void getSongWithRanges( @ApiParam(name = "songSid", description = "SID of the song resource we want to download", paramType = ApiParamType.PATH, required = true) @PathVariable Long songSid, HttpServletRequest request, HttpServletResponse response) throws NotAuthorizedMessicRESTException, IOMessicRESTException, UnknownMessicRESTException { User user = SecurityUtil.getCurrentUser(); try { //http://balusc.blogspot.com.es/2009/02/fileservlet-supporting-resume-and.html // Whether the request body should be written (GET) or not (HEAD). boolean content = request.getMethod().equalsIgnoreCase("GET"); APISong.AudioSongStream ass = songAPI.getAudioSong(user, songSid); // Validate and process Range and If-Range headers. String eTag = songSid + "_" + ass.contentLength + "_" + ass.lastModified; long expires = System.currentTimeMillis() + Range.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 && Range.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 > ass.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 && !Range.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 <= ass.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, ass.contentLength - 1, ass.contentLength); List<Range> ranges = new ArrayList<Range>(); 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 */" + ass.contentLength); // 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 < ass.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 = Range.sublong(part, 0, part.indexOf("-")); long end = Range.sublong(part, part.indexOf("-") + 1, part.length()); if (start == -1) { start = ass.contentLength - end; end = ass.contentLength - 1; } else if (end == -1 || end > ass.contentLength - 1) { end = ass.contentLength - 1; } // Check if Range is syntactically valid. If not, then return 416. if (start > end) { response.setHeader("Content-Range", "bytes */" + ass.contentLength); // Required in 416. response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); return; } // Add range. ranges.add(new Range(start, end, ass.contentLength)); } } } // Prepare and initialize response -------------------------------------------------------- // Get content type by file name and set default GZIP support and content disposition. String contentType = "audio/mpeg"; 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 && Range.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 && Range.accepts(accept, contentType) ? "inline" : "attachment"; } // Initialize response. response.reset(); response.setBufferSize(Range.DEFAULT_BUFFER_SIZE); response.setHeader("Content-Disposition", disposition + ";filename=\"" + ass.songFileName + "\""); response.setHeader("Accept-Ranges", "bytes"); response.setHeader("ETag", eTag); response.setDateHeader("Last-Modified", ass.lastModified); response.setDateHeader("Expires", expires); // Send requested file (part(s)) to client ------------------------------------------------ // Prepare streams. OutputStream output = null; try { // Open streams. 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, Range.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. Range.copy(ass.raf, 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. Range.copy(ass.raf, output, r.start, r.length); } } else { // Return multiple parts of file. response.setContentType("multipart/byteranges; boundary=" + Range.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("--" + Range.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. Range.copy(ass.raf, output, r.start, r.length); } // End with multipart boundary. sos.println(); sos.println("--" + Range.MULTIPART_BOUNDARY + "--"); } } } finally { // Gently close streams. Range.close(output); Range.close(ass.is); Range.close(ass.raf); } return; } catch (IOException ioe) { log.error("failed!", ioe); throw new IOMessicRESTException(ioe); } catch (Exception e) { throw new UnknownMessicRESTException(e); } }
From source file:com.xpn.xwiki.web.DownloadActionTest.java
@Test public void testOneByteRange() throws XWikiException, IOException { // This test expects the last four bytes (10, 11, 12 and 13) from the file final Date d = new Date(); createAttachment(d, DEFAULT_FILE_NAME); setRequestExpectations(DEFAULT_URI, null, null, "bytes=0-0", -1l); setResponseExpectations(d.getTime(), 1); setOutputExpectations(0, 1);/*from ww w . j a v a 2 s .c o 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 0-0/" + DownloadActionTest.this.fileContent.length)); } }); Assert.assertNull(this.action.render(getContext())); }
From source file:org.jahia.services.content.files.StaticFileServlet.java
/** * Process the actual request./*from w w w . j a va 2 s .com*/ * * @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 ------------------------------------------------------------ // Get requested file by path info. String requestedFile = request.getPathInfo(); // 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(basePath, URLDecoder.decode(requestedFile, "UTF-8")); // Check if file actually exists in filesystem. if (!file.exists() || !file.isFile()) { // 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; } // Verify the file requested is a descendant of the base directory. if (!file.getCanonicalPath().startsWith(basePath)) { 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 (!PATTERN_RANGE.matcher(range).matches()) { 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 : Patterns.COMMA.split(range.substring(6))) { // 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 = enableGzip && 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); } }
From source file:com.xpn.xwiki.web.DownloadActionTest.java
@Test public void testRestRange() throws XWikiException, IOException { // This test expects bytes from 11 to the end of the file (11, 12 and 13) final Date d = new Date(); createAttachment(d, DEFAULT_FILE_NAME); setRequestExpectations(DEFAULT_URI, null, null, "bytes=11-", -1l); setResponseExpectations(d.getTime(), this.fileContent.length - 11); setOutputExpectations(11, this.fileContent.length); getMockery().checking(new Expectations() { {//from w w w .j a v a2 s . c o m one(DownloadActionTest.this.response).setStatus(with(HttpServletResponse.SC_PARTIAL_CONTENT)); one(DownloadActionTest.this.response).setHeader(with("Content-Range"), with("bytes 11-13/" + DownloadActionTest.this.fileContent.length)); } }); Assert.assertNull(this.action.render(getContext())); }
From source file:com.jpeterson.littles3.StorageEngine.java
/** * Process HTTP HEAD and GET/*from w w w . ja v a2s . c om*/ * * @param req * an HttpServletRequest object that contains the request the * client has made of the servlet * @param resp * an HttpServletResponse object that contains the response the * servlet sends to the client * @throws IOException * if an input or output error is detected when the servlet * handles the GET request * @throws ServletException * if the request for the GET could not be handled */ @SuppressWarnings("unchecked") public void processHeadGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { if (logger.isDebugEnabled()) { logger.debug("Context path: " + req.getContextPath()); logger.debug("Path info: " + req.getPathInfo()); logger.debug("Path translated: " + req.getPathTranslated()); logger.debug("Query string: " + req.getQueryString()); logger.debug("Request URI: " + req.getRequestURI()); logger.debug("Request URL: " + req.getRequestURL()); logger.debug("Servlet path: " + req.getServletPath()); logger.debug("Servlet name: " + this.getServletName()); for (Enumeration headerNames = req.getHeaderNames(); headerNames.hasMoreElements();) { String headerName = (String) headerNames.nextElement(); String headerValue = req.getHeader(headerName); logger.debug("Header- " + headerName + ": " + headerValue); } } try { S3ObjectRequest or; try { or = S3ObjectRequest.create(req, resolvedHost(), (Authenticator) getWebApplicationContext().getBean(BEAN_AUTHENTICATOR)); } catch (InvalidAccessKeyIdException e) { e.printStackTrace(); resp.sendError(HttpServletResponse.SC_FORBIDDEN, "InvalidAccessKeyId"); return; } catch (InvalidSecurityException e) { e.printStackTrace(); resp.sendError(HttpServletResponse.SC_FORBIDDEN, "InvalidSecurity"); return; } catch (RequestTimeTooSkewedException e) { e.printStackTrace(); resp.sendError(HttpServletResponse.SC_FORBIDDEN, "RequestTimeTooSkewed"); return; } catch (SignatureDoesNotMatchException e) { e.printStackTrace(); resp.sendError(HttpServletResponse.SC_FORBIDDEN, "SignatureDoesNotMatch"); return; } catch (AuthenticatorException e) { e.printStackTrace(); resp.sendError(HttpServletResponse.SC_FORBIDDEN, "InvalidSecurity"); return; } if (or.getKey() != null) { S3Object s3Object; StorageService storageService; try { storageService = (StorageService) getWebApplicationContext().getBean(BEAN_STORAGE_SERVICE); s3Object = storageService.load(or.getBucket(), or.getKey()); if (s3Object == null) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, "NoSuchKey"); return; } } catch (DataAccessException e) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, "NoSuchKey"); return; } if (req.getParameter(PARAMETER_ACL) != null) { // retrieve access control policy String response; Acp acp = s3Object.getAcp(); try { acp.canRead(or.getRequestor()); } catch (AccessControlException e) { resp.sendError(HttpServletResponse.SC_FORBIDDEN, "AccessDenied"); return; } response = Acp.encode(acp); resp.setContentLength(response.length()); resp.setContentType("application/xml"); resp.setStatus(HttpServletResponse.SC_OK); Writer out = resp.getWriter(); out.write(response); out.flush(); // commit response out.close(); out = null; } else { // retrieve object InputStream in = null; OutputStream out = null; byte[] buffer = new byte[4096]; int count; String value; try { s3Object.canRead(or.getRequestor()); } catch (AccessControlException e) { resp.sendError(HttpServletResponse.SC_FORBIDDEN, "AccessDenied"); return; } // headers resp.setContentType(s3Object.getContentType()); if ((value = s3Object.getContentDisposition()) != null) { resp.setHeader("Content-Disposition", value); } // TODO: set the Content-Range, if request includes Range // TODO: add "x-amz-missing-meta", if any // add the "x-amz-meta-" headers for (Iterator<String> names = s3Object.getMetadataNames(); names.hasNext();) { String name = names.next(); String headerName = HEADER_PREFIX_USER_META + name; String prefix = ""; StringBuffer buf = new StringBuffer(); for (Iterator<String> values = s3Object.getMetadataValues(name); values.hasNext();) { buf.append(values.next()).append(prefix); prefix = ","; } resp.setHeader(headerName, buf.toString()); } resp.setDateHeader("Last-Modified", s3Object.getLastModified()); if ((value = s3Object.getETag()) != null) { resp.setHeader("ETag", value); } if ((value = s3Object.getContentMD5()) != null) { resp.setHeader("Content-MD5", value); } if ((value = s3Object.getContentDisposition()) != null) { resp.setHeader("Content-Disposition", value); } resp.setHeader("Accept-Ranges", "bytes"); String rangeRequest = req.getHeader("Range"); if (rangeRequest != null) { // request for a range RangeSet rangeSet = RangeFactory.processRangeHeader(rangeRequest); // set content length rangeSet.resolve(s3Object.getContentLength()); if (rangeSet.size() > 1) { // requires multi-part response // TODO: implement resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED); } Range[] ranges = (Range[]) rangeSet.toArray(new Range[0]); resp.setHeader("Content-Range", formatRangeHeaderValue(ranges[0], s3Object.getContentLength())); resp.setHeader("Content-Length", Long.toString(rangeSet.getLength())); in = new RangeInputStream(s3Object.getInputStream(), ranges[0]); resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); } else { // request for entire content // Used instead of resp.setContentLength((int)); because // Amazon // limit is 5 gig, which is bigger than an int resp.setHeader("Content-Length", Long.toString(s3Object.getContentLength())); in = s3Object.getInputStream(); resp.setStatus(HttpServletResponse.SC_OK); } // body out = resp.getOutputStream(); while ((count = in.read(buffer, 0, buffer.length)) > 0) { out.write(buffer, 0, count); } out.flush(); // commit response out.close(); out = null; } return; } else if (or.getBucket() != null) { // operation on a bucket StorageService storageService; String prefix; String marker; int maxKeys = Integer.MAX_VALUE; String delimiter; String response; String value; storageService = (StorageService) getWebApplicationContext().getBean(BEAN_STORAGE_SERVICE); if (req.getParameter(PARAMETER_ACL) != null) { // retrieve access control policy Acp acp; try { acp = storageService.loadBucket(or.getBucket()).getAcp(); } catch (DataAccessException e) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, "NoSuchBucket"); return; } try { acp.canRead(or.getRequestor()); } catch (AccessControlException e) { resp.sendError(HttpServletResponse.SC_FORBIDDEN, "AccessDenied"); return; } response = Acp.encode(acp); resp.setContentLength(response.length()); resp.setContentType("application/xml"); resp.setStatus(HttpServletResponse.SC_OK); Writer out = resp.getWriter(); out.write(response); out.flush(); // commit response out.close(); out = null; } else { Bucket bucket; prefix = req.getParameter("prefix"); if (prefix == null) { prefix = ""; } marker = req.getParameter("marker"); value = req.getParameter("max-keys"); if (value != null) { try { maxKeys = Integer.parseInt(value); } catch (NumberFormatException e) { logger.info("max-keys must be numeric: " + value); } } delimiter = req.getParameter("delimiter"); try { bucket = storageService.loadBucket(or.getBucket()); } catch (DataAccessException e) { resp.sendError(HttpServletResponse.SC_NOT_FOUND, "NoSuchBucket"); return; } try { bucket.canRead(or.getRequestor()); } catch (AccessControlException e) { resp.sendError(HttpServletResponse.SC_FORBIDDEN, "AccessDenied"); return; } response = storageService.listKeys(bucket, prefix, marker, delimiter, maxKeys); resp.setContentLength(response.length()); resp.setContentType("application/xml"); resp.setStatus(HttpServletResponse.SC_OK); Writer out = resp.getWriter(); out.write(response); if (logger.isTraceEnabled()) { logger.trace("Response: " + response); } } return; } else { // operation on the service StorageService storageService; List buckets; storageService = (StorageService) getWebApplicationContext().getBean(BEAN_STORAGE_SERVICE); buckets = storageService.findBuckets(""); StringBuffer buffer = new StringBuffer(); buffer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); buffer.append("<ListAllMyBucketsResult xmlns=\"http://s3.amazonaws.com/doc/2006-03-01/\">"); buffer.append("<Owner>"); buffer.append("<ID/>"); // TODO: implement buffer.append("<DisplayName/>"); // TODO: implementF buffer.append("</Owner>"); buffer.append("<Buckets>"); for (Iterator iter = buckets.iterator(); iter.hasNext();) { Bucket bucket = (Bucket) iter.next(); buffer.append("<Bucket>"); buffer.append("<Name>").append(bucket.getName()).append("</Name>"); buffer.append("<CreationDate>").append(iso8601.format(bucket.getCreated())) .append("</CreationDate>"); buffer.append("</Bucket>"); } buffer.append("</Buckets>"); buffer.append("</ListAllMyBucketsResult>"); resp.setContentLength(buffer.length()); resp.setContentType("application/xml"); resp.setStatus(HttpServletResponse.SC_OK); Writer out = resp.getWriter(); out.write(buffer.toString()); return; } } catch (IllegalArgumentException e) { resp.sendError(HttpServletResponse.SC_BAD_REQUEST, "InvalidURI"); return; } }