6#include <aws/core/http/HttpRequest.h>
7#include <aws/core/utils/Array.h>
8#include <aws/core/utils/StringUtils.h>
9#include <aws/core/utils/HashingUtils.h>
10#include <aws/core/utils/logging/LogMacros.h>
11#include <aws/core/utils/memory/stl/AWSStringStream.h>
12#include <aws/core/utils/memory/stl/AWSVector.h>
13#include <smithy/interceptor/Interceptor.h>
14#include <aws/core/client/ClientConfiguration.h>
15#include <aws/core/utils/Outcome.h>
16#include <aws/core/client/AWSError.h>
27template <
size_t DataBufferSize = AWS_DATA_BUFFER_SIZE>
31 const std::shared_ptr<Aws::IOStream>& stream,
32 size_t bufferSize = DataBufferSize)
37 assert(m_stream !=
nullptr);
38 if (m_stream ==
nullptr) {
39 AWS_LOGSTREAM_ERROR(
"AwsChunkedStream",
"stream is null");
41 assert(m_request !=
nullptr);
42 if (m_request ==
nullptr) {
43 AWS_LOGSTREAM_ERROR(
"AwsChunkedStream",
"request is null");
49 if (gptr() && gptr() < egptr()) {
50 return traits_type::to_int_type(*gptr());
55 if (m_stream->good() && m_chunkingBufferPos >= m_chunkingBufferSize) {
57 m_chunkingBufferPos = 0;
58 m_chunkingBufferSize = 0;
61 size_t maxChunkSize = m_data.
GetLength() + 20;
62 if (m_chunkingBufferSize + maxChunkSize <= m_chunkingBuffer.
GetLength()) {
65 size_t bytesRead =
static_cast<size_t>(m_stream->gcount());
66 writeChunk(bytesRead);
70 if ((m_stream->peek() == EOF || m_stream->eof()) && !m_stream->bad()) {
71 writeTrailerToUnderlyingStream();
77 if (m_chunkingBufferPos >= m_chunkingBufferSize) {
78 return traits_type::eof();
82 size_t remainingBytes = m_chunkingBufferSize - m_chunkingBufferPos;
83 size_t bytesToRead = (std::min)(remainingBytes, DataBufferSize);
89 m_chunkingBufferPos += bytesToRead;
91 return traits_type::to_int_type(*gptr());
95 void writeTrailerToUnderlyingStream() {
98 trailer +=
"x-amz-checksum-" + m_request->
GetRequestHash().first +
":"
102 if (m_chunkingBufferSize + trailer.length() <= m_chunkingBuffer.
GetLength()) {
103 std::memcpy(m_chunkingBuffer.
GetUnderlyingData() + m_chunkingBufferSize, trailer.c_str(), trailer.length());
104 m_chunkingBufferSize += trailer.length();
108 void writeChunk(
size_t bytesRead) {
115 size_t totalSize = chunkHeader.length() + bytesRead + 2;
116 if (m_chunkingBufferSize + totalSize <= m_chunkingBuffer.
GetLength()) {
117 std::memcpy(m_chunkingBuffer.
GetUnderlyingData() + m_chunkingBufferSize, chunkHeader.c_str(), chunkHeader.length());
118 m_chunkingBufferSize += chunkHeader.length();
120 m_chunkingBufferSize += bytesRead;
121 std::memcpy(m_chunkingBuffer.
GetUnderlyingData() + m_chunkingBufferSize,
"\r\n", 2);
122 m_chunkingBufferSize += 2;
129 size_t m_chunkingBufferSize{0};
130 size_t m_chunkingBufferPos{0};
132 std::shared_ptr<Aws::IOStream> m_stream;
139 const std::shared_ptr<Aws::IOStream>& originalBody,
141 :
Aws::IOStream(&m_buf),
142 m_buf(request, originalBody, bufferSize) {}
157 : m_httpClientChunkedMode(httpClientChunkedMode), m_bufferSize(bufferSize) {}
161 if (m_bufferSize < 8 * 1024) {
163 "ValidationErrorException",
164 "aws-chunked buffer must be over 8KiB to content encode",
169 if (!ShouldApplyChunking(request, context)) {
173 const auto& hashPair = request->GetRequestHash();
174 if (hashPair.second !=
nullptr) {
176 request->DeleteHeader(checksumHeaderValue.c_str());
180 if (!request->HasContentEncoding()) {
183 Aws::String currentEncoding = request->GetContentEncoding();
199 if (m_bufferSize < 8 * 1024) {
201 "ValidationErrorException",
202 "aws-chunked buffer must be over 8KiB to content encode",
207 if (!ShouldApplyChunking(request, context)) {
211 auto originalBody = request->GetContentBody();
216 auto chunkedBody = Aws::MakeShared<AwsChunkedIOStream>(
ALLOCATION_TAG, request.get(), originalBody, m_bufferSize);
218 request->AddContentBody(chunkedBody);
227 bool ShouldApplyChunking(
const std::shared_ptr<Aws::Http::HttpRequest>& request,
230 const auto signerName = context.
GetAttribute(
"signer_name");
231 if (signerName.has_value() && signerName.value() ==
"AsymmetricSignatureV4") {
239 if (!request || !request->GetContentBody()) {
244 const auto& hashPair = request->GetRequestHash();
245 return hashPair.second !=
nullptr;
const std::pair< Aws::String, std::shared_ptr< Aws::Utils::Crypto::Hash > > & GetRequestHash() const
T * GetUnderlyingData() const
static Aws::String Base64Encode(const ByteBuffer &byteBuffer)
static Aws::String ToHexString(T value)
AwsChunkedIOStream(Aws::Http::HttpRequest *request, const std::shared_ptr< Aws::IOStream > &originalBody, size_t bufferSize=AWS_DATA_BUFFER_SIZE)
int_type underflow() override
AwsChunkedStreamBuf(Aws::Http::HttpRequest *request, const std::shared_ptr< Aws::IOStream > &stream, size_t bufferSize=DataBufferSize)
ModifyRequestOutcome ModifyBeforeTransmit(smithy::interceptor::InterceptorContext &context) override
ModifyResponseOutcome ModifyBeforeDeserialization(smithy::interceptor::InterceptorContext &context) override
ChunkingInterceptor(Aws::Client::HttpClientChunkedMode httpClientChunkedMode, size_t bufferSize)
~ChunkingInterceptor() override=default
ModifyRequestOutcome ModifyBeforeSigning(smithy::interceptor::InterceptorContext &context) override
ChunkingInterceptor(Aws::Client::HttpClientChunkedMode httpClientChunkedMode)
Aws::Crt::Optional< Aws::String > GetAttribute(const Aws::String &key) const
std::shared_ptr< Aws::Http::HttpRequest > GetTransmitRequest() const
std::shared_ptr< Aws::Http::HttpResponse > GetTransmitResponse() const
AWS_CORE_API const char CHUNKED_VALUE[]
AWS_CORE_API const char DECODED_CONTENT_LENGTH_HEADER[]
AWS_CORE_API const char CONTENT_LENGTH_HEADER[]
AWS_CORE_API const char AWS_CHUNKED_VALUE[]
AWS_CORE_API const char AWS_TRAILER_HEADER[]
std::basic_iostream< char, std::char_traits< char > > IOStream
std::basic_string< char, std::char_traits< char >, Aws::Allocator< char > > String
static const char * ALLOCATION_TAG
static const char * CHECKSUM_HEADER_PREFIX
static const size_t AWS_DATA_BUFFER_SIZE