From e6b611d33dd454dc0f8507aa5be783d289965df7 Mon Sep 17 00:00:00 2001 From: Michael Herdt Date: Tue, 16 Mar 2021 14:15:22 +0100 Subject: [PATCH 1/2] Add a method to cancel an Artifact Input Stream before it is closed. Signed-off-by: Michael Herdt --- .../repository/model/AbstractDbArtifact.java | 10 ++++- .../hawkbit/rest/util/FileStreamingUtil.java | 45 +++++++++++-------- 2 files changed, 36 insertions(+), 19 deletions(-) diff --git a/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/AbstractDbArtifact.java b/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/AbstractDbArtifact.java index 6746c1d640..2a77d32497 100644 --- a/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/AbstractDbArtifact.java +++ b/hawkbit-core/src/main/java/org/eclipse/hawkbit/artifact/repository/model/AbstractDbArtifact.java @@ -64,8 +64,16 @@ public String getContentType() { /** * Creates an {@link InputStream} on this artifact. Caller has to take care * of closing the stream. Repeatable calls open a new {@link InputStream}. - * + * * @return {@link InputStream} to read from artifact. */ public abstract InputStream getFileInputStream(); + + /** + * Used to abort the stream before it is finally closed. This is needed when the + * download is aborted, for example. + */ + public void abortIfNeeded() { + // do abort input stream if needed + } } diff --git a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java index 98ea2454a3..55b34f4f32 100644 --- a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java +++ b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java @@ -195,17 +195,31 @@ private static ResponseEntity handleFullFileRequest(final AbstractD final ByteRange r = full; response.setHeader(HttpHeaders.CONTENT_RANGE, "bytes " + r.getStart() + "-" + r.getEnd() + "/" + r.getTotal()); response.setContentLengthLong(r.getLength()); - - try (InputStream from = artifact.getFileInputStream()) { - final ServletOutputStream to = response.getOutputStream(); - copyStreams(from, to, progressListener, r.getStart(), r.getLength(), filename); + try { + read(artifact, filename, response, progressListener, r); } catch (final IOException e) { throw new FileStreamingFailedException("fullfileRequest " + filename, e); } - return ResponseEntity.ok().build(); } + private static void read(final AbstractDbArtifact artifact, final String filename, final HttpServletResponse response, + final FileStreamingProgressListener progressListener, final ByteRange r) throws IOException { + read(artifact, filename, response.getOutputStream(), progressListener, r); + } + + private static void read(final AbstractDbArtifact artifact, final String filename, final ServletOutputStream to, + final FileStreamingProgressListener progressListener, final ByteRange r) throws IOException { + InputStream from = artifact.getFileInputStream(); + try { + copyStreams(from, to, progressListener, r.getStart(), r.getLength(), filename); + } catch (final IOException e) { + artifact.abortIfNeeded(); + } finally { + from.close(); + } + } + private static ResponseEntity extractRange(final HttpServletResponse response, final long length, final List ranges, final String range) { @@ -268,17 +282,13 @@ private static ResponseEntity handleMultipartRangeRequest(final Abs final ServletOutputStream to = response.getOutputStream(); for (final ByteRange r : ranges) { - try (InputStream from = artifact.getFileInputStream()) { - - // Add multipart boundary and header fields for every range. - to.println(); - to.println("--" + ByteRange.MULTIPART_BOUNDARY); - to.println(HttpHeaders.CONTENT_RANGE + ": bytes " + r.getStart() + "-" + r.getEnd() + "/" - + r.getTotal()); + // Add multipart boundary and header fields for every range. + to.println(); + to.println("--" + ByteRange.MULTIPART_BOUNDARY); + to.println( + HttpHeaders.CONTENT_RANGE + ": bytes " + r.getStart() + "-" + r.getEnd() + "/" + r.getTotal()); - // Copy single part range of multi part range. - copyStreams(from, to, progressListener, r.getStart(), r.getLength(), filename); - } + read(artifact, filename, to, progressListener, r); } // End with final multipart boundary. @@ -299,9 +309,8 @@ private static ResponseEntity handleStandardRangeRequest(final Abst response.setContentLengthLong(r.getLength()); response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); - try (InputStream from = artifact.getFileInputStream()) { - final ServletOutputStream to = response.getOutputStream(); - copyStreams(from, to, progressListener, r.getStart(), r.getLength(), filename); + try { + read(artifact, filename, response, progressListener, r); } catch (final IOException e) { LOG.error("standardRangeRequest of file ({}) failed!", filename, e); throw new FileStreamingFailedException(filename); From 995064fcdc4b6c6a7ad236ebb619ad25c719f027 Mon Sep 17 00:00:00 2001 From: Michael Herdt Date: Tue, 16 Mar 2021 14:40:07 +0100 Subject: [PATCH 2/2] Rethrow IOException on input stream exception Signed-off-by: Michael Herdt --- .../java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java index 55b34f4f32..65a22bb582 100644 --- a/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java +++ b/hawkbit-rest/hawkbit-rest-core/src/main/java/org/eclipse/hawkbit/rest/util/FileStreamingUtil.java @@ -215,6 +215,7 @@ private static void read(final AbstractDbArtifact artifact, final String filenam copyStreams(from, to, progressListener, r.getStart(), r.getLength(), filename); } catch (final IOException e) { artifact.abortIfNeeded(); + throw e; } finally { from.close(); }