0

I am using wildfly-9 with java-8 on windows OS. browser: chrome and firefox For download file implemented Range support. Refer some of the article related range support

  1. https://github.com/spring-projects/spring-framework/blob/v4.2.0.RC1/spring- webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java#L463
  2. How to Implement HTTP byte-range requests in Spring MVC
  3. how to resume an interrupted download

Implemented code fore reference Request from initial servlet

if (httpRequest.getHeader(HttpHeaders.RANGE) == null) {

    setHeaders(httpResponse, contentType, contentDisposition, contentEncoding, asrFile.getSize());
    writeContent(httpResponse, asrFile.getInputStream());
}
else {
    writePartialContent(httpRequest, httpResponse, asrFile.getInputStream(), contentType, contentDisposition,contentEncoding,asrFile.getSize());
}

Implementation for file download to support range

protected void writePartialContent(HttpServletRequest request, HttpServletResponse response,
            InputStream in, String contentType, String contentDisposition, String contentEncoding, long contentLength) throws IOException {

        long length = contentLength;

        List<HttpRange> ranges;
        try {
            HttpHeaders headers = new ServletServerHttpRequest(request).getHeaders();
            ranges = headers.getRange();
        }
        catch (IllegalArgumentException ex) {
            response.addHeader("Content-Range", "bytes */" + length);
            response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            return;
        }

        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

        if (ranges.size() == 1) {
            HttpRange range = ranges.get(0);

            long start = range.getRangeStart(length);
            long end = range.getRangeEnd(length);
            long rangeLength = end - start + 1;

            setHeaders(response, contentType,contentDisposition,contentEncoding,contentLength);
            response.addHeader("Content-Range", "bytes " + start + "-" + end + "/" + length);
            response.setContentLength((int) rangeLength);

            try {
                copyRange(in, response.getOutputStream(), start, end);
            }
            finally {
                try {
                    in.close();
                }
                catch (IOException ex) {
                    log.error("IOException while closing:"+ex,ex);
                }
            }
        }
        else {
            String boundaryString = MimeTypeUtils.generateMultipartBoundaryString();
            response.setContentType("multipart/byteranges; boundary=" + boundaryString);

            ServletOutputStream out = response.getOutputStream();

            for (HttpRange range : ranges) {
                long start = range.getRangeStart(length);
                long end = range.getRangeEnd(length);

                // Writing MIME header.
                out.println();
                out.println("--" + boundaryString);
                if (contentType != null) {
                    out.println("Content-Type: " + contentType);
                }
                out.println("Content-Range: bytes " + start + "-" + end + "/" + length);
                out.println();

                // Printing content
                copyRange(in, out, start, end);
            }
            out.println();
            out.print("--" + boundaryString + "--");
        }
    }
    
    
    private void copyRange(InputStream in, OutputStream out, long start, long end) throws IOException {

        long skipped = in.skip(start);

        if (skipped < start) {
            throw new IOException("Skipped only " + skipped + " bytes out of " + start + " required.");
        }

        long bytesToCopy = end - start + 1;

        byte buffer[] = new byte[StreamUtils.BUFFER_SIZE];
        while (bytesToCopy > 0) {
            int bytesRead = in.read(buffer);
            if (bytesRead <= bytesToCopy) {
                out.write(buffer, 0, bytesRead);
                bytesToCopy -= bytesRead;
            }
            else {
                out.write(buffer, 0, (int) bytesToCopy);
                bytesToCopy = 0;
            }
            if (bytesRead < buffer.length) {
                break;
            }
        }
    }
    
    protected void setHeaders(HttpServletResponse response, String contentType, String contentDisposition, String contentEncoding, long contentLength) throws IOException {
        response.setContentLength((int) contentLength);

        if (contentType != null) {
            response.setContentType(contentType);
        }

        if(contentEncoding != null) {
            response.setHeader("Content-Encoding", contentEncoding);
        }
        response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
        response.setHeader("Content-Disposition", contentDisposition);
        
        //note: setting unique value per download file
        response.setHeader("ETag", ETag);               

    }
    
    
    protected void writeContent(HttpServletResponse response, InputStream in) throws IOException {
        try {
            try {
                StreamUtils.copy(in, response.getOutputStream());
            }
            finally {
                try {
                    in.close();
                }
                catch (Throwable ex) {
                    log.error("Throwable while closing:"+ex,ex);
                }
            }
        }
        catch (FileNotFoundException ex) {
            log.error("FileNotFoundException while closing:"+ex,ex);
        }
    }

to check its working or not I am shutdown server in my system & notice some of bytes are corrupted in downloaded file.

1st line with junk character 2nd line was original content enter image description here Apache Log details

+----+------------+------+-------------------------------+-----------------------------------------+-------------------+------------------------+-----------------------------------------+
| No |    Host    | - -  |          Date&Time            |           "Request details"             | HTTP status code  | content lenght(bytes)  |               Description               |
+----+------------+------+-------------------------------+-----------------------------------------+-------------------+------------------------+-----------------------------------------+
|  1 | 127.0.0.1  | - -  | [07/Nov/2022:18:41:06 +0530]  | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1"  |              502  |              15466666  | (when server is stopped)                |
|  2 | 127.0.0.1  | - -  | [07/Nov/2022:18:42:14 +0530]  | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1"  |              503  |                   323  | (after server is stopped)               |
|  3 | 127.0.0.1  | - -  | [07/Nov/2022:18:48:14 +0530]  | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1"  |              206  |              27905015  | (after re-start server partial content) |
+----+------------+------+-------------------------------+-----------------------------------------+-------------------+------------------------+-----------------------------------------+

I have doubt on second request, however content size is not match (323) with that junk char size.

I have also check with Download manager(single thread) enter image description here

Apache logs


+----+------------+-------------------------------+-----------------------------------------+-------------------+------------------------+---------------------------------+
| No |    Host    |          Date&Time            |           "Request details"             | HTTP status code  | content lenght(bytes)  |           Description           |
+----+------------+-------------------------------+-----------------------------------------+-------------------+------------------------+---------------------------------+
|  1 | 127.0.0.1  | [07/Nov/2022:19:00:49 +0530]  | "HEAD /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1" |              502  |                      - | Initial request                 |
|  2 | 127.0.0.1  | [07/Nov/2022:19:01:35 +0530]  | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1"  |              503  |                    323 | Once server is shutdown         |
|  3 | 127.0.0.1  | [07/Nov/2022:19:05:18 +0530]  | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1"  |              200  |                  40952 | Once server re-start done       |
|  4 | 127.0.0.1  | [07/Nov/2022:19:05:44 +0530]  | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1"  |              206  |               33672353 | another partial content request |
+----+------------+-------------------------------+-----------------------------------------+-------------------+------------------------+---------------------------------+

Seems like below request is served by apache when server is shutdown(offline)

+----+------------+-------------------------------+-----------------------------------------+-------------------+------------------------+-------------------------+
| No |    Host    |          Date&Time            |           "Request details"             | HTTP status code  | content lenght(bytes)  |       Description       |
+----+------------+-------------------------------+-----------------------------------------+-------------------+------------------------+-------------------------+
|  2 | 127.0.0.1  | [07/Nov/2022:19:01:35 +0530]  | "GET /lnk/x7y7gUMpa99bCz4Boe HTTP/1.1"  |              503  |                    323 | Once server is shutdown |
+----+------------+-------------------------------+-----------------------------------------+-------------------+------------------------+-------------------------+

Is there any configuration to avoid these junk characters/data or any details so I can troubleshoot this issue further.

mcacorner
  • 1,304
  • 3
  • 22
  • 45
  • 1
    The apache involvement is unclear to me. In case it's a reverse proxy: The 50x error response comes with content - e.g. a human readable message. That might well be your content-length. – Olaf Kock Nov 07 '22 at 14:38

0 Answers0