77
88#include < aws/core/Core_EXPORTS.h>
99#include < aws/core/client/ClientConfiguration.h>
10- #include < aws/core/utils/stream/AwsChunkedStream.h>
10+ #include < aws/core/utils/HashingUtils.h>
11+ #include < aws/core/utils/StringUtils.h>
12+ #include < aws/core/utils/Array.h>
1113#include < smithy/interceptor/Interceptor.h>
1214#include < smithy/client/common/AwsSmithyClientUtils.h>
1315
@@ -17,50 +19,107 @@ namespace features {
1719
1820namespace {
1921
20- // String constants
2122static const char * const CHECKSUM_HEADER_PREFIX = " x-amz-checksum-" ;
2223static const char * const ALLOCATION_TAG = " ChunkingInterceptor" ;
24+ static const size_t DATA_BUFFER_SIZE = 65536 ;
2325
24- class AwsChunkedStreamBuf : public std ::streambuf {
26+ } // anonymous namespace
27+
28+ class AwsChunkedStreamWrapper {
2529public:
26- AwsChunkedStreamBuf (Aws::Http::HttpRequest* request, const std::shared_ptr<Aws::IOStream>& originalBody)
27- : m_chunkStream(request, originalBody) {
28- setg (nullptr , nullptr , nullptr );
29- }
30+ AwsChunkedStreamWrapper (Aws::Http::HttpRequest* request, const std::shared_ptr<Aws::IOStream>& originalBody, size_t bufferSize = DATA_BUFFER_SIZE)
31+ : m_streambuf(request, originalBody, bufferSize), m_iostream(&m_streambuf) {}
32+
33+ Aws::IOStream* GetIOStream () { return &m_iostream; }
34+
35+ private:
36+ class AwsChunkedStreamBuf : public std ::streambuf {
37+ public:
38+ AwsChunkedStreamBuf (Aws::Http::HttpRequest* request, const std::shared_ptr<Aws::IOStream>& originalBody, size_t bufferSize)
39+ : m_request(request), m_stream(originalBody), m_data(bufferSize), m_bufferSize(bufferSize),
40+ m_chunkingStream (Aws::MakeShared<Aws::StringStream>(ALLOCATION_TAG)) {
41+ setg (nullptr , nullptr , nullptr );
42+ }
43+
44+ protected:
45+ int_type underflow () override {
46+ if (gptr () < egptr ()) {
47+ return traits_type::to_int_type (*gptr ());
48+ }
3049
31- protected:
32- int_type underflow () override {
33- if (gptr () < egptr ()) {
50+ if (m_stream->good ()) {
51+ m_stream->read (m_data.GetUnderlyingData (), m_bufferSize);
52+ size_t bytesRead = static_cast <size_t >(m_stream->gcount ());
53+ writeChunk (bytesRead);
54+
55+ if ((m_stream->peek () == EOF || m_stream->eof ()) && !m_stream->bad ()) {
56+ writeTrailerToUnderlyingStream ();
57+ }
58+ }
59+
60+ if ((m_chunkingStream->peek () == EOF || m_chunkingStream->eof ()) && !m_chunkingStream->bad ()) {
61+ return traits_type::eof ();
62+ }
63+
64+ m_chunkingStream->read (m_buffer, sizeof (m_buffer));
65+ size_t bytesRead = static_cast <size_t >(m_chunkingStream->gcount ());
66+ if (bytesRead == 0 ) {
67+ return traits_type::eof ();
68+ }
69+
70+ setg (m_buffer, m_buffer, m_buffer + bytesRead);
3471 return traits_type::to_int_type (*gptr ());
3572 }
36-
37- size_t bytesRead = m_chunkStream.BufferedRead (m_buffer, sizeof (m_buffer));
38- if (bytesRead == 0 ) {
39- return traits_type::eof ();
73+
74+ private:
75+ void writeTrailerToUnderlyingStream () {
76+ Aws::StringStream chunkedTrailerStream;
77+ chunkedTrailerStream << " 0\r\n " ;
78+ if (m_request->GetRequestHash ().second != nullptr ) {
79+ chunkedTrailerStream << " x-amz-checksum-" << m_request->GetRequestHash ().first << " :"
80+ << Aws::Utils::HashingUtils::Base64Encode (m_request->GetRequestHash ().second ->GetHash ().GetResult ()) << " \r\n " ;
4081 }
41-
42- setg (m_buffer, m_buffer, m_buffer + bytesRead);
43- return traits_type::to_int_type (*gptr ());
82+ chunkedTrailerStream << " \r\n " ;
83+ const auto chunkedTrailer = chunkedTrailerStream.str ();
84+ if (m_chunkingStream->eof ()) {
85+ m_chunkingStream->clear ();
86+ }
87+ *m_chunkingStream << chunkedTrailer;
4488 }
45-
46- std::streamsize showmanyc () override {
47- return egptr () - gptr ();
89+
90+ void writeChunk (size_t bytesRead) {
91+ if (m_request->GetRequestHash ().second != nullptr ) {
92+ m_request->GetRequestHash ().second ->Update (reinterpret_cast <unsigned char *>(m_data.GetUnderlyingData ()), bytesRead);
93+ }
94+
95+ if (bytesRead > 0 && m_chunkingStream != nullptr && !m_chunkingStream->bad ()) {
96+ if (m_chunkingStream->eof ()) {
97+ m_chunkingStream->clear ();
98+ }
99+ *m_chunkingStream << Aws::Utils::StringUtils::ToHexString (bytesRead) << " \r\n " ;
100+ m_chunkingStream->write (m_data.GetUnderlyingData (), bytesRead);
101+ *m_chunkingStream << " \r\n " ;
102+ }
48103 }
49104
50- private:
51- Aws::Utils::Stream::AwsChunkedStream<> m_chunkStream;
52- char m_buffer[8192 ];
105+ Aws::Http::HttpRequest* m_request;
106+ std::shared_ptr<Aws::IOStream> m_stream;
107+ Aws::Utils::Array<char > m_data;
108+ size_t m_bufferSize;
109+ std::shared_ptr<Aws::IOStream> m_chunkingStream;
110+ char m_buffer[8192 ];
111+ };
112+
113+ AwsChunkedStreamBuf m_streambuf;
114+ Aws::IOStream m_iostream;
53115};
54116
55- } // anonymous namespace
56-
57117/* *
58118 * Interceptor that handles chunked encoding for streaming requests with checksums.
59119 * Wraps request body with chunked stream and sets appropriate headers.
60120 */
61121class ChunkingInterceptor : public smithy ::interceptor::Interceptor {
62122public:
63- ChunkingInterceptor () : m_httpClientChunkedMode(Aws::Client::HttpClientChunkedMode::DEFAULT) {}
64123 explicit ChunkingInterceptor (const Aws::Client::ClientConfiguration& config)
65124 : m_httpClientChunkedMode(config.httpClientChunkedMode) {}
66125 ~ChunkingInterceptor () override = default ;
@@ -100,11 +159,11 @@ class ChunkingInterceptor : public smithy::interceptor::Interceptor {
100159 }
101160 }
102161
103- auto chunkedBuf = Aws::MakeUnique<AwsChunkedStreamBuf >(
162+ auto wrapper = Aws::MakeShared<AwsChunkedStreamWrapper >(
104163 ALLOCATION_TAG, request.get (), originalBody);
105164 auto chunkedBody = std::shared_ptr<Aws::IOStream>(
106- new Aws::IOStream (chunkedBuf. release () ));
107-
165+ wrapper, wrapper-> GetIOStream ( ));
166+
108167 request->AddContentBody (chunkedBody);
109168 return request;
110169 }
0 commit comments