From b7dee6dbd07e24c5da284ccdd2dbd7b51d4a8bb1 Mon Sep 17 00:00:00 2001 From: Abdul Haleem Sheikh Date: Mon, 18 Nov 2024 10:34:41 -0800 Subject: [PATCH 1/4] feat: Track bytes in rep service output stream --- .../java/com/box/sdk/BinaryBodyUtils.java | 49 +++++++++++++++++++ src/main/java/com/box/sdk/BoxFile.java | 3 +- src/test/java/com/box/sdk/BoxFileTest.java | 2 + 3 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/box/sdk/BinaryBodyUtils.java b/src/main/java/com/box/sdk/BinaryBodyUtils.java index 58c4bcb24..9f34db3f7 100644 --- a/src/main/java/com/box/sdk/BinaryBodyUtils.java +++ b/src/main/java/com/box/sdk/BinaryBodyUtils.java @@ -46,6 +46,28 @@ static void writeStream(BoxAPIResponse response, OutputStream output, ProgressLi } } + /** + * Writes response body bytes to output stream. After all closes the input stream. + * + * @param response Response that is going to be written. + * @param output Output stream. + * @param listener Listener that will be notified on writing response. Can be null. + */ + + static void writeStreamWithContentLength(BoxAPIResponse response, OutputStream output, ProgressListener listener) { + try { + InputStream input; + if (listener != null) { + input = response.getBody(listener); + } else { + input = response.getBody(); + } + writeStreamTo(input, output, response.getContentLength()); + } finally { + response.close(); + } + } + /** * Writes content of input stream to provided output. Method is NOT closing input stream. * @@ -71,4 +93,31 @@ static void writeStreamTo(InputStream input, OutputStream output) { } } } + + static void writeStreamTo(InputStream input, OutputStream output, long expectedLength) { + long totalBytesRead = 0; + if (expectedLength < 0) { + throw new RuntimeException("No Data bytes in stream"); + } + try { + byte[] buffer = new byte[8192]; + for (int n = input.read(buffer); n != -1; n = input.read(buffer)) { + output.write(buffer, 0, n); + totalBytesRead += n; // Track the total bytes read + } + if (totalBytesRead != expectedLength) { + throw new IOException("Stream ended prematurely. Expected " + expectedLength + + " bytes, but read " + totalBytesRead + " bytes."); + } + } catch (IOException e) { + throw new RuntimeException("Error during streaming: " + e.getMessage(), e); + } finally { + try { + input.close(); + output.close(); + } catch (IOException closeException) { + throw new RuntimeException("IOException during stream close", closeException); + } + } + } } diff --git a/src/main/java/com/box/sdk/BoxFile.java b/src/main/java/com/box/sdk/BoxFile.java index bcb62cf15..d0dad40b5 100644 --- a/src/main/java/com/box/sdk/BoxFile.java +++ b/src/main/java/com/box/sdk/BoxFile.java @@ -1,6 +1,7 @@ package com.box.sdk; import static com.box.sdk.BinaryBodyUtils.writeStream; +import static com.box.sdk.BinaryBodyUtils.writeStreamWithContentLength; import static com.box.sdk.http.ContentType.APPLICATION_JSON; import static com.box.sdk.http.ContentType.APPLICATION_JSON_PATCH; import static com.eclipsesource.json.Json.NULL; @@ -602,7 +603,7 @@ private void makeRepresentationContentRequest( URL repURL = new URL(representationURLTemplate.replace("{+asset_path}", assetPath)); BoxAPIRequest repContentReq = new BoxAPIRequest(this.getAPI(), repURL, HttpMethod.GET); BoxAPIResponse response = repContentReq.send(); - writeStream(response, output); + writeStreamWithContentLength(response, output, null); } catch (MalformedURLException ex) { throw new BoxAPIException("Could not generate representation content URL"); diff --git a/src/test/java/com/box/sdk/BoxFileTest.java b/src/test/java/com/box/sdk/BoxFileTest.java index 6d642c638..62f7479bb 100644 --- a/src/test/java/com/box/sdk/BoxFileTest.java +++ b/src/test/java/com/box/sdk/BoxFileTest.java @@ -433,6 +433,7 @@ public void testGetThumbnailSucceeds() { )) .willReturn(WireMock.aResponse() .withHeader("Content-Type", "image/jpg") + .withHeader("Content-Length", String.valueOf(13)) .withBody("This is a JPG") .withStatus(200)) ); @@ -517,6 +518,7 @@ public void testGetRepresentationContentSuccess() { )) .willReturn(WireMock.aResponse() .withHeader("Content-Type", "image/jpg") + .withHeader("Content-Length", String.valueOf(13)) .withBody("This is a JPG") .withStatus(200)) ); From 346849a5addb05f8beb29fa9aa4376eedc3d0940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Socha?= <31014760+lukaszsocha2@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:23:40 +0100 Subject: [PATCH 2/4] Update BinaryBodyUtils.java --- src/main/java/com/box/sdk/BinaryBodyUtils.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/box/sdk/BinaryBodyUtils.java b/src/main/java/com/box/sdk/BinaryBodyUtils.java index 9f34db3f7..42d15aa3c 100644 --- a/src/main/java/com/box/sdk/BinaryBodyUtils.java +++ b/src/main/java/com/box/sdk/BinaryBodyUtils.java @@ -46,6 +46,17 @@ static void writeStream(BoxAPIResponse response, OutputStream output, ProgressLi } } + /** + * Writes response body bytes to output stream. After all closes the input stream. + * + * @param response Response that is going to be written. + * @param output Output stream. + */ + + static void writeStreamWithContentLength(BoxAPIResponse response, OutputStream output) { + writeStreamWithContentLength(response, output, null); + } + /** * Writes response body bytes to output stream. After all closes the input stream. * @@ -97,10 +108,10 @@ static void writeStreamTo(InputStream input, OutputStream output) { static void writeStreamTo(InputStream input, OutputStream output, long expectedLength) { long totalBytesRead = 0; if (expectedLength < 0) { - throw new RuntimeException("No Data bytes in stream"); + throw new RuntimeException("Expected content length should not be negative: " + expectedLength); } try { - byte[] buffer = new byte[8192]; + byte[] buffer = new byte[BUFFER_SIZE]; for (int n = input.read(buffer); n != -1; n = input.read(buffer)) { output.write(buffer, 0, n); totalBytesRead += n; // Track the total bytes read From 763b6063704a345074b5069d7e6ddd8df22e99d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Socha?= <31014760+lukaszsocha2@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:24:06 +0100 Subject: [PATCH 3/4] Update BoxFile.java --- src/main/java/com/box/sdk/BoxFile.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/box/sdk/BoxFile.java b/src/main/java/com/box/sdk/BoxFile.java index d0dad40b5..ca3d9883d 100644 --- a/src/main/java/com/box/sdk/BoxFile.java +++ b/src/main/java/com/box/sdk/BoxFile.java @@ -603,7 +603,7 @@ private void makeRepresentationContentRequest( URL repURL = new URL(representationURLTemplate.replace("{+asset_path}", assetPath)); BoxAPIRequest repContentReq = new BoxAPIRequest(this.getAPI(), repURL, HttpMethod.GET); BoxAPIResponse response = repContentReq.send(); - writeStreamWithContentLength(response, output, null); + writeStreamWithContentLength(response, output); } catch (MalformedURLException ex) { throw new BoxAPIException("Could not generate representation content URL"); From 0f199548c34eb53b68181cdbe6f1cbddae7cf02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Socha?= <31014760+lukaszsocha2@users.noreply.github.com> Date: Thu, 21 Nov 2024 13:29:34 +0100 Subject: [PATCH 4/4] add docstring --- src/main/java/com/box/sdk/BinaryBodyUtils.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/box/sdk/BinaryBodyUtils.java b/src/main/java/com/box/sdk/BinaryBodyUtils.java index 42d15aa3c..002b3b5a6 100644 --- a/src/main/java/com/box/sdk/BinaryBodyUtils.java +++ b/src/main/java/com/box/sdk/BinaryBodyUtils.java @@ -80,7 +80,7 @@ static void writeStreamWithContentLength(BoxAPIResponse response, OutputStream o } /** - * Writes content of input stream to provided output. Method is NOT closing input stream. + * Writes content of input stream to provided output. * * @param input Input that will be read. * @param output Output stream. @@ -105,6 +105,15 @@ static void writeStreamTo(InputStream input, OutputStream output) { } } + /** + * Writes the content of the input stream to the provided output stream, ensuring the exact number of bytes specified + * by the expected length is written. If the stream ends prematurely an exception is thrown. + * + * @param input The input stream to be read. + * @param output The output stream where data will be written. + * @param expectedLength The expected number of bytes to be transferred. + */ + static void writeStreamTo(InputStream input, OutputStream output, long expectedLength) { long totalBytesRead = 0; if (expectedLength < 0) {