• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License").
5  * You may not use this file except in compliance with the License.
6  * A copy of the License is located at
7  *
8  *  http://aws.amazon.com/apache2.0
9  *
10  * or in the "license" file accompanying this file. This file is distributed
11  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12  * express or implied. See the License for the specific language governing
13  * permissions and limitations under the License.
14  */
15 
16 package software.amazon.awssdk.core.internal.util;
17 
18 import java.nio.ByteBuffer;
19 import java.nio.charset.StandardCharsets;
20 import software.amazon.awssdk.annotations.SdkInternalApi;
21 import software.amazon.awssdk.core.checksums.Algorithm;
22 import software.amazon.awssdk.core.exception.SdkClientException;
23 
24 @SdkInternalApi
25 public final class ChunkContentUtils {
26 
27     public static final String HEADER_COLON_SEPARATOR = ":";
28     public static final String ZERO_BYTE = "0";
29     public static final String CRLF = "\r\n";
30 
31     public static final String LAST_CHUNK = ZERO_BYTE + CRLF;
32     public static final long LAST_CHUNK_LEN = LAST_CHUNK.length();
33 
ChunkContentUtils()34     private ChunkContentUtils() {
35     }
36 
37     /**
38      * The chunk format is: chunk-size CRLF chunk-data CRLF.
39      *
40      * @param originalContentLength Original Content length.
41      * @return the length of this chunk
42      */
calculateChunkLength(long originalContentLength)43     public static long calculateChunkLength(long originalContentLength) {
44         if (originalContentLength == 0) {
45             return 0;
46         }
47         return Long.toHexString(originalContentLength).length()
48                + CRLF.length()
49                + originalContentLength
50                + CRLF.length();
51     }
52 
53     /**
54      * Calculates the content length for data that is divided into chunks.
55      *
56      * @param originalLength  original content length.
57      * @param chunkSize chunk size
58      * @return Content length of the trailer that will be appended at the end.
59      */
calculateStreamContentLength(long originalLength, long chunkSize)60     public static long calculateStreamContentLength(long originalLength, long chunkSize) {
61         if (originalLength < 0 || chunkSize == 0) {
62             throw new IllegalArgumentException(originalLength + ", " + chunkSize + "Args <= 0 not expected");
63         }
64 
65         long maxSizeChunks = originalLength / chunkSize;
66         long remainingBytes = originalLength % chunkSize;
67 
68         long allChunks = maxSizeChunks * calculateChunkLength(chunkSize);
69         long remainingInChunk = remainingBytes > 0 ? calculateChunkLength(remainingBytes) : 0;
70         // last byte is composed of a "0" and "\r\n"
71         long lastByteSize = 1 + (long) CRLF.length();
72 
73         return allChunks +  remainingInChunk + lastByteSize;
74     }
75 
76     /**
77      * Calculates the content length for a given algorithm and header name.
78      *
79      * @param algorithm  Algorithm used.
80      * @param headerName Header name.
81      * @return Content length of the trailer that will be appended at the end.
82      */
calculateChecksumTrailerLength(Algorithm algorithm, String headerName)83     public static long calculateChecksumTrailerLength(Algorithm algorithm, String headerName) {
84         return headerName.length()
85                + HEADER_COLON_SEPARATOR.length()
86                + algorithm.base64EncodedLength().longValue()
87                + CRLF.length()
88                + CRLF.length();
89     }
90 
91     /**
92      * Creates Chunk encoded checksum trailer for a computedChecksum which is in Base64 encoded.
93      * @param computedChecksum Base64 encoded computed checksum.
94      * @param trailerHeader Header for the checksum data in the trailer.
95      * @return Chunk encoded checksum trailer with given header.
96      */
createChecksumTrailer(String computedChecksum, String trailerHeader)97     public static ByteBuffer createChecksumTrailer(String computedChecksum, String trailerHeader) {
98         StringBuilder headerBuilder = new StringBuilder(trailerHeader)
99                 .append(HEADER_COLON_SEPARATOR)
100                 .append(computedChecksum)
101                 .append(CRLF)
102                 .append(CRLF);
103         return ByteBuffer.wrap(headerBuilder.toString().getBytes(StandardCharsets.UTF_8));
104     }
105 
106     /**
107      * Creates ChunkEncoded data for an given chunk data.
108      * @param chunkData chunk data that needs to be converted to chunk encoded format.
109      * @param isLastByte if true then additional CRLF will not be appended.
110      * @return Chunk encoded format of a given data.
111      */
createChunk(ByteBuffer chunkData, boolean isLastByte)112     public static ByteBuffer createChunk(ByteBuffer chunkData, boolean isLastByte) {
113         int chunkLength = chunkData.remaining();
114         StringBuilder chunkHeader = new StringBuilder(Integer.toHexString(chunkLength));
115         chunkHeader.append(CRLF);
116         try {
117             byte[] header = chunkHeader.toString().getBytes(StandardCharsets.UTF_8);
118             byte[] trailer = !isLastByte ? CRLF.getBytes(StandardCharsets.UTF_8)
119                     : "".getBytes(StandardCharsets.UTF_8);
120             ByteBuffer chunkFormattedBuffer = ByteBuffer.allocate(header.length + chunkLength + trailer.length);
121             chunkFormattedBuffer.put(header).put(chunkData).put(trailer);
122             chunkFormattedBuffer.flip();
123             return chunkFormattedBuffer;
124         } catch (Exception e) {
125             throw SdkClientException.builder()
126                     .message("Unable to create chunked data. " + e.getMessage())
127                     .cause(e)
128                     .build();
129         }
130     }
131 }