• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

box / box-java-sdk / #4179

26 Nov 2024 01:33PM CUT coverage: 71.751% (-0.02%) from 71.773%
#4179

Pull #1277

github

web-flow
Merge 0153e0c5a into fc1a3e3da
Pull Request #1277: fix: Correctly calculate `Content-Length` when reading from a stream

5 of 10 new or added lines in 1 file covered. (50.0%)

7 existing lines in 1 file now uncovered.

8077 of 11257 relevant lines covered (71.75%)

0.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.09
/src/main/java/com/box/sdk/BinaryBodyUtils.java
1
package com.box.sdk;
2

3
import java.io.IOException;
4
import java.io.InputStream;
5
import java.io.OutputStream;
6

7
/**
8
 * Utility class to help writing binary data to output stream.
9
 */
10
final class BinaryBodyUtils {
11
    private static final int BUFFER_SIZE = 8192;
12
    private static final String X_ORIGINAL_CONTENT_LENGTH = "X-Original-Content-Length";
13

14
    private BinaryBodyUtils() {
15
        // utility class has no public constructor
16
    }
17

18
    /**
19
     * Writes response body bytes to output stream. After all closes the input stream.
20
     *
21
     * @param response Response that is going to be written.
22
     * @param output   Output stream.
23
     */
24
    static void writeStream(BoxAPIResponse response, OutputStream output) {
25
        writeStream(response, output, null);
1✔
26
    }
1✔
27

28
    /**
29
     * Writes response body bytes to output stream. After all closes the input stream.
30
     *
31
     * @param response Response that is going to be written.
32
     * @param output   Output stream.
33
     * @param listener Listener that will be notified on writing response. Can be null.
34
     */
35

36
    static void writeStream(BoxAPIResponse response, OutputStream output, ProgressListener listener) {
37
        try {
38
            InputStream input;
39
            if (listener != null) {
1✔
40
                input = response.getBody(listener);
×
41
            } else {
42
                input = response.getBody();
1✔
43
            }
44
            writeStreamTo(input, output);
1✔
45
        } finally {
46
            response.close();
1✔
47
        }
48
    }
1✔
49

50
    /**
51
     * Writes response body bytes to output stream. After all closes the input stream.
52
     *
53
     * @param response Response that is going to be written.
54
     * @param output   Output stream.
55
     */
56

57
    static void writeStreamWithContentLength(BoxAPIResponse response, OutputStream output) {
58
        writeStreamWithContentLength(response, output, null);
1✔
59
    }
1✔
60

61
    /**
62
     * Writes response body bytes to output stream. After all closes the input stream.
63
     *
64
     * @param response Response that is going to be written.
65
     * @param output   Output stream.
66
     * @param listener Listener that will be notified on writing response. Can be null.
67
     */
68

69
    static void writeStreamWithContentLength(BoxAPIResponse response, OutputStream output, ProgressListener listener) {
70
        try {
71
            InputStream input;
72
            if (listener != null) {
1✔
73
                input = response.getBody(listener);
×
74
            } else {
75
                input = response.getBody();
1✔
76
            }
77
            writeStreamTo(input, output, getContentLengthFromAPIResponse(response));
1✔
78
        } finally {
79
            response.close();
1✔
80
        }
81
    }
1✔
82

83
    /**
84
     * Get the content length from the API response.
85
     * In some cases, the Content-Length is not provided in the response headers.
86
     * This could happen when getting the content representation for a compressed data.
87
     * In that case the API will switch to chunk mode and provide the length in the "X-Original-Content-Length" header.
88
     *
89
     * @param response API response.
90
     * @return Content length.
91
     */
92
    private static long getContentLengthFromAPIResponse(BoxAPIResponse response) {
93
        long length = response.getContentLength();
1✔
94
        try {
95
            if (length == -1 && response.getHeaders().containsKey(X_ORIGINAL_CONTENT_LENGTH)) {
1✔
NEW
96
                length = Integer.parseInt(response.getHeaders().get(X_ORIGINAL_CONTENT_LENGTH).get(0));
×
97
            }
NEW
98
        } catch (NumberFormatException e) {
×
NEW
99
            throw new RuntimeException(
×
NEW
100
                "Invalid content length: " + response.getHeaders().get("X-Original-Content-Length"
×
NEW
101
                ).get(0));
×
102
        }
1✔
103

104
        return length;
1✔
105
    }
106

107
    /**
108
     * Writes content of input stream to provided output.
109
     *
110
     * @param input  Input that will be read.
111
     * @param output Output stream.
112
     */
113
    static void writeStreamTo(InputStream input, OutputStream output) {
114
        try {
115
            byte[] buffer = new byte[BUFFER_SIZE];
1✔
116
            int n = input.read(buffer);
1✔
117
            while (n != -1) {
1✔
118
                output.write(buffer, 0, n);
1✔
119
                n = input.read(buffer);
1✔
120
            }
UNCOV
121
        } catch (IOException e) {
×
UNCOV
122
            throw new RuntimeException(e);
×
123
        } finally {
124
            try {
125
                input.close();
1✔
126
                output.close();
1✔
UNCOV
127
            } catch (IOException e) {
×
128
                throw new RuntimeException(e);
×
129
            }
1✔
130
        }
131
    }
1✔
132

133
    /**
134
     * Writes the content of the input stream to the provided output stream, ensuring the exact number of bytes specified
135
     * by the expected length is written. If the stream ends prematurely an exception is thrown.
136
     *
137
     * @param input          The input stream to be read.
138
     * @param output         The output stream where data will be written.
139
     * @param expectedLength The expected number of bytes to be transferred.
140
     */
141

142
    static void writeStreamTo(InputStream input, OutputStream output, long expectedLength) {
143
        long totalBytesRead = 0;
1✔
144
        if (expectedLength < 0) {
1✔
UNCOV
145
            throw new RuntimeException("Expected content length should not be negative: " + expectedLength);
×
146
        }
147
        try {
148
            byte[] buffer = new byte[BUFFER_SIZE];
1✔
149
            for (int n = input.read(buffer); n != -1; n = input.read(buffer)) {
1✔
150
                output.write(buffer, 0, n);
1✔
151
                totalBytesRead += n;  // Track the total bytes read
1✔
152
            }
153
            if (totalBytesRead != expectedLength) {
1✔
UNCOV
154
                throw new IOException("Stream ended prematurely. Expected " + expectedLength
×
155
                    + " bytes, but read " + totalBytesRead + " bytes.");
156
            }
UNCOV
157
        } catch (IOException e) {
×
UNCOV
158
            throw new RuntimeException("Error during streaming: " + e.getMessage(), e);
×
159
        } finally {
160
            try {
161
                input.close();
1✔
162
                output.close();
1✔
163
            } catch (IOException closeException) {
×
164
                throw new RuntimeException("IOException during stream close", closeException);
×
165
            }
1✔
166
        }
167
    }
1✔
168
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc