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

box / box-java-sdk / #4211

27 Nov 2024 12:50PM CUT coverage: 71.717% (-0.06%) from 71.773%
#4211

Pull #1277

github

web-flow
Merge 365a523de into 26b05bdba
Pull Request #1277: fix: Correctly calculate `Content-Length` when reading from a stream

4 of 14 new or added lines in 1 file covered. (28.57%)

2 existing lines in 1 file now uncovered.

8076 of 11261 relevant lines covered (71.72%)

0.72 hits per line

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

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

3
import com.box.sdk.http.HttpHeaders;
4
import java.io.IOException;
5
import java.io.InputStream;
6
import java.io.OutputStream;
7

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

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

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

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

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

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

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

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

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

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

111
        return length;
1✔
112
    }
113

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

140
    /**
141
     * Writes the content of the input stream to the provided output stream, ensuring the exact number of bytes specified
142
     * by the expected length is written. If the stream ends prematurely an exception is thrown.
143
     *
144
     * @param input          The input stream to be read.
145
     * @param output         The output stream where data will be written.
146
     * @param expectedLength The expected number of bytes to be transferred.
147
     */
148

149
    static void writeStreamTo(InputStream input, OutputStream output, long expectedLength) {
150
        long totalBytesRead = 0;
1✔
151
        if (expectedLength < 0) {
1✔
152
            throw new RuntimeException("Expected content length should not be negative: " + expectedLength);
×
153
        }
154
        try {
155
            byte[] buffer = new byte[BUFFER_SIZE];
1✔
156
            for (int n = input.read(buffer); n != -1; n = input.read(buffer)) {
1✔
157
                output.write(buffer, 0, n);
1✔
158
                totalBytesRead += n;  // Track the total bytes read
1✔
159
            }
160
            if (totalBytesRead != expectedLength) {
1✔
161
                throw new IOException("Stream ended prematurely. Expected " + expectedLength
×
162
                    + " bytes, but read " + totalBytesRead + " bytes.");
163
            }
164
        } catch (IOException e) {
×
165
            throw new RuntimeException("Error during streaming: " + e.getMessage(), e);
×
166
        } finally {
167
            try {
168
                input.close();
1✔
169
                output.close();
1✔
170
            } catch (IOException closeException) {
×
171
                throw new RuntimeException("IOException during stream close", closeException);
×
172
            }
1✔
173
        }
174
    }
1✔
175
}
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