123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- /**
- * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
- * SPDX-License-Identifier: Apache-2.0.
- */
- #include <aws/core/client/RequestCompression.h>
- #include <aws/core/utils/logging/LogMacros.h>
- #include <aws/core/utils/memory/AWSMemory.h>
- #include <algorithm>
- #include <aws/core/utils/memory/stl/AWSAllocator.h>
- #ifdef ENABLED_ZLIB_REQUEST_COMPRESSION
- #include "zlib.h"
- #if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
- #include <fcntl.h>
- #include <io.h>
- #define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
- #else
- #define SET_BINARY_MODE(file)
- #endif // defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
- // Defining zlib chunks to be 256k
- static const size_t ZLIB_CHUNK=263144;
- static const char AWS_REQUEST_COMPRESSION_ALLOCATION_TAG[] =
- "RequestCompressionAlloc";
- #endif // ENABLED_ZLIB_REQUEST_COMPRESSION
- static const char AWS_REQUEST_COMPRESSION_LOG_TAG[] = "RequestCompression";
- Aws::String Aws::Client::GetCompressionAlgorithmId(const CompressionAlgorithm &algorithm)
- {
- switch (algorithm)
- {
- case CompressionAlgorithm::GZIP:
- return "gzip";
- default:
- return "";
- }
- }
- #ifdef ENABLED_ZLIB_REQUEST_COMPRESSION
- #ifdef USE_AWS_MEMORY_MANAGEMENT
- static const char* ZlibMemTag = "zlib";
- static const size_t offset = sizeof(size_t); // to make space for size of the array
- //Define custom memory allocation for zlib
- // if fail to allocate, return Z_NULL
- void* aws_zalloc(void * /* opaque */, unsigned items, unsigned size)
- {
- unsigned sizeToAllocate = items*size;
- size_t sizeToAllocateWithOffset = sizeToAllocate + offset;
- if ((size != 0 && sizeToAllocate / size != items)
- || (sizeToAllocateWithOffset <= sizeToAllocate ))
- {
- return Z_NULL;
- }
- char* newMem = reinterpret_cast<char*>(Aws::Malloc(ZlibMemTag, sizeToAllocateWithOffset));
- if (newMem != nullptr) {
- std::size_t* pointerToSize = reinterpret_cast<std::size_t*>(newMem);
- *pointerToSize = size;
- return reinterpret_cast<void*>(newMem + offset);
- }
- else
- {
- return Z_NULL;
- }
- }
- void aws_zfree(void * /* opaque */, void * ptr)
- {
- if(ptr)
- {
- char* shiftedMemory = reinterpret_cast<char*>(ptr);
- Aws::Free(shiftedMemory - offset);
- }
- }
- #endif // AWS_CUSTOM_MEMORY_MANAGEMENT
- #endif // ENABLED_ZLIB_REQUEST_COMPRESSION
- iostream_outcome Aws::Client::RequestCompression::compress(std::shared_ptr<Aws::IOStream> input,
- const CompressionAlgorithm &algorithm) const
- {
- #ifdef ENABLED_ZLIB_REQUEST_COMPRESSION
- if (algorithm == CompressionAlgorithm::GZIP)
- {
- // calculating stream size
- input->seekg(0, input->end);
- size_t streamSize = input->tellg();
- input->seekg(0, input->beg);
- AWS_LOGSTREAM_TRACE(AWS_REQUEST_COMPRESSION_LOG_TAG, "Compressing request of " << streamSize << " bytes.");
- // Preparing output
- std::shared_ptr<Aws::IOStream> output = Aws::MakeShared<Aws::StringStream>(AWS_REQUEST_COMPRESSION_ALLOCATION_TAG);
- if(!output)
- {
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Failed to allocate output while compressing")
- return false;
- }
- // Prepare ZLIB to compress
- int ret = Z_NULL;
- int flush = Z_NO_FLUSH;
- z_stream strm = {};
- auto in = Aws::MakeUniqueArray<unsigned char>(ZLIB_CHUNK, AWS_REQUEST_COMPRESSION_ALLOCATION_TAG);
- if(!in)
- {
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Failed to allocate in buffer while compressing")
- return false;
- }
- auto out = Aws::MakeUniqueArray<unsigned char>(ZLIB_CHUNK, AWS_REQUEST_COMPRESSION_ALLOCATION_TAG);
- if(!out)
- {
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Failed to allocate out buffer while compressing")
- return false;
- }
- //Preparing allocators
- #ifdef USE_AWS_MEMORY_MANAGEMENT
- strm.zalloc = (void *(*)(void *, unsigned, unsigned)) aws_zalloc;
- strm.zfree = (void (*)(void *, void *)) aws_zfree;
- #else
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- #endif
- strm.opaque = Z_NULL;
- const int MAX_WINDOW_GZIP = 31;
- const int DEFAULT_MEM_LEVEL_USAGE = 8;
- ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WINDOW_GZIP, DEFAULT_MEM_LEVEL_USAGE, Z_DEFAULT_STRATEGY);
- if(ret != Z_OK)
- {
- return false;
- }
- //Adding one to the stream size counter to account for the EOF marker.
- streamSize++;
- size_t toRead;
- // Compress
- do {
- toRead = std::min(streamSize, ZLIB_CHUNK);
- // Fill the buffer
- if (! input->read(reinterpret_cast<char *>(in.get()), toRead))
- {
- if (input->eof())
- {
- //Last read need flush when exit loop
- flush = Z_FINISH;
- }
- else {
- AWS_LOGSTREAM_ERROR(
- AWS_REQUEST_COMPRESSION_LOG_TAG,
- "Uncompress request failed to read from stream");
- return false;
- }
- }
- streamSize -= toRead; //left to read
- strm.avail_in = (flush == Z_FINISH)?toRead-1:toRead; //skip EOF if included
- strm.next_in = in.get();
- do
- {
- // Run deflate on buffers
- strm.avail_out = ZLIB_CHUNK;
- strm.next_out = out.get();
- ret = deflate(&strm, flush);
- // writing the output
- assert(ZLIB_CHUNK >= strm.avail_out);
- unsigned output_size = ZLIB_CHUNK - strm.avail_out;
- if(! output->write(reinterpret_cast<char *>(out.get()), output_size))
- {
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Compressed request failed to write to output stream");
- return false;
- }
- } while (strm.avail_out == 0);
- assert(strm.avail_in == 0); // All data was read
- } while (flush != Z_FINISH);
- assert(ret == Z_STREAM_END); // Completed stream
- AWS_LOGSTREAM_TRACE(AWS_REQUEST_COMPRESSION_LOG_TAG, "Compressed request to: " << strm.total_out << " bytes");
- deflateEnd(&strm);
- return output;
- }
- else
- {
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Compress request requested in runtime without support: " << GetCompressionAlgorithmId(algorithm));
- return false;
- }
- #else
- // If there is no support to compress, always fail calls to this method.
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Compress request requested in runtime without support: " << GetCompressionAlgorithmId(algorithm));
- AWS_UNREFERENCED_PARAM(input); // silencing warning;
- return false;
- #endif
- }
- Aws::Utils::Outcome<std::shared_ptr<Aws::IOStream>, bool>
- Aws::Client::RequestCompression::uncompress(std::shared_ptr<Aws::IOStream> input, const CompressionAlgorithm &algorithm) const
- {
- #ifdef ENABLED_ZLIB_REQUEST_COMPRESSION
- if (algorithm == CompressionAlgorithm::GZIP)
- {
- // calculating stream size
- input->seekg(0, input->end);
- size_t streamSize = input->tellg();
- input->seekg(0, input->beg);
- AWS_LOGSTREAM_TRACE(AWS_REQUEST_COMPRESSION_LOG_TAG, "Uncompressing request of " << streamSize << " bytes.");
- // Preparing output
- std::shared_ptr<Aws::IOStream> output = Aws::MakeShared<Aws::StringStream>( AWS_REQUEST_COMPRESSION_ALLOCATION_TAG);
- if(!output)
- {
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Failed to allocate output while uncompressing")
- return false;
- }
- // Prepare ZLIB to uncompress
- int ret = Z_NULL;
- z_stream strm = {};
- auto in = Aws::MakeUniqueArray<unsigned char>(ZLIB_CHUNK, AWS_REQUEST_COMPRESSION_ALLOCATION_TAG);
- if(!in)
- {
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Failed to allocate in buffer while uncompressing")
- return false;
- }
- auto out = Aws::MakeUniqueArray<unsigned char>(ZLIB_CHUNK, AWS_REQUEST_COMPRESSION_ALLOCATION_TAG);
- if(!out)
- {
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Failed to allocate out buffer while uncompressing")
- return false;
- }
- //preparing allocation
- #ifdef USE_AWS_MEMORY_MANAGEMENT
- strm.zalloc = (void *(*)(void *, unsigned, unsigned)) aws_zalloc;
- strm.zfree = (void (*)(void *, void *)) aws_zfree;
- #else
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- #endif
- strm.opaque = Z_NULL;
- strm.avail_in = 0;
- strm.next_in = Z_NULL;
- const int MAX_WINDOW_GZIP = 31;
- ret = inflateInit2(&strm, MAX_WINDOW_GZIP);
- if (ret != Z_OK)
- {
- return false;
- }
- //Adding one to the stream size counter to account for the EOF marker.
- streamSize++;
- size_t toRead;
- // Decompress
- do {
- toRead = (streamSize < ZLIB_CHUNK)?streamSize:ZLIB_CHUNK;
- if (toRead < 1) break; // Nothing left to read
- // Fill the buffer
- if(! input->read(reinterpret_cast<char *>(in.get()), toRead))
- {
- if (input->eof())
- {
- //skip passing the EOF to the buffer
- toRead--;
- }
- else
- {
- AWS_LOGSTREAM_ERROR(
- AWS_REQUEST_COMPRESSION_LOG_TAG,
- "Compress request failed to read from stream");
- return false;
- }
- }
- // Filling input buffer to decompress
- strm.avail_in = toRead;
- strm.next_in = in.get();
- do
- {
- // Run inflate on buffers
- strm.avail_out = ZLIB_CHUNK;
- strm.next_out = out.get();
- ret = inflate(&strm, Z_NO_FLUSH);
- // Catch errors
- switch (ret)
- {
- case Z_NEED_DICT:
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Compressed request failed to inflate with code: Z_NEED_DICT");
- return false;
- case Z_DATA_ERROR:
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Compressed request failed to inflate with code: Z_DATA_ERROR");
- return false;
- case Z_MEM_ERROR:
- (void)inflateEnd(&strm);
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Compressed request failed to inflate with code: Z_MEM_ERROR");
- return false;
- }
- // writing the output
- unsigned output_size = ZLIB_CHUNK - strm.avail_out;
- if(! output->write(reinterpret_cast<char *>(out.get()), output_size)) {
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Uncompressed request failed to write to output stream");
- return false;
- }
- } while (strm.avail_out == 0);
- } while (ret != Z_STREAM_END);
- // clean up
- (void)inflateEnd(&strm);
- if (ret == Z_STREAM_END)
- {
- AWS_LOGSTREAM_TRACE(AWS_REQUEST_COMPRESSION_LOG_TAG, "Decompressed request to: " << strm.total_out << " bytes");
- return output;
- }
- else
- {
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Failed to decompress after read input completely");
- return false;
- }
- }
- else
- {
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Uncompress request requested in runtime without support: " << GetCompressionAlgorithmId(algorithm));
- return false;
- }
- #else
- // If there is no support to compress, always fail calls to this method.
- AWS_LOGSTREAM_ERROR(AWS_REQUEST_COMPRESSION_LOG_TAG, "Uncompress request requested in runtime without support: " << GetCompressionAlgorithmId(algorithm));
- AWS_UNREFERENCED_PARAM(input); // silencing warning;
- return false;
- #endif
- }
|