in app/rest/zlibwrapper.cc [504:681]
int ZLib::UncompressAtMostOrAll(Bytef* dest, uLongf* destLen,
const Bytef* source, uLong* sourceLen,
int flush_mode) { // Z_SYNC_FLUSH or Z_FINISH
int err = Z_OK;
if (first_chunk_) {
gzip_footer_bytes_ = -1;
if (settings_.gzip_header_mode_) {
// If we haven't read our first chunk of actual compressed data,
// and we're expecting gzip headers, then parse some more bytes
// from the gzip headers.
const Bytef* bodyBegin = nullptr;
GZipHeader::Status status = gzip_header_->ReadMore(
reinterpret_cast<const char*>(source), *sourceLen,
reinterpret_cast<const char**>(&bodyBegin));
switch (status) {
case GZipHeader::INCOMPLETE_HEADER: // don't have the complete header
*destLen = 0;
return Z_OK;
case GZipHeader::INVALID_HEADER: // bogus header
Reset();
return Z_DATA_ERROR;
case GZipHeader::COMPLETE_HEADER: // we have the full header
*sourceLen -= (bodyBegin - source); // skip past header bytes
source = bodyBegin;
crc_ = crc32(0, nullptr, 0); // initialize CRC
break;
default:
LogError("Unexpected gzip header parsing result: %s", status);
}
}
} else if (gzip_footer_bytes_ >= 0) {
// We're now just reading the gzip footer. We already read all the data.
if (gzip_footer_bytes_ + *sourceLen > sizeof(gzip_footer_) &&
// When this flag is true, we allow some extra bytes after the
// gzip footer.
!should_be_flexible_with_gzip_footer_) {
LogDebug(
"UncompressChunkOrAll: Received %d extra bytes after gzip footer: %s",
gzip_footer_bytes_ + *sourceLen - sizeof(gzip_footer_),
reinterpret_cast<const char*>(source), std::min(*sourceLen, 20UL));
Reset();
return Z_DATA_ERROR;
}
uLong len = sizeof(gzip_footer_) - gzip_footer_bytes_;
if (len > *sourceLen) len = *sourceLen;
if (len > 0) {
memcpy(gzip_footer_ + gzip_footer_bytes_, source, len);
gzip_footer_bytes_ += len;
}
*destLen = 0;
return Z_OK;
}
if ((err = UncompressInit(dest, destLen, source, sourceLen)) != Z_OK) {
LogWarning("ZLib: UncompressInit: Error: %d SourceLen: %d", err,
*sourceLen);
return err;
}
// This is used to figure out how many output bytes we wrote *this chunk*:
const uLong old_total_out = uncomp_stream_.total_out;
// This is used to figure out how many input bytes we read *this chunk*:
const uLong old_total_in = uncomp_stream_.total_in;
// Some setup happens only for the first chunk we compress in a run
if (first_chunk_) {
// Initialize the dictionary just before we start compressing
if (settings_.gzip_header_mode_ || settings_.no_header_mode_) {
// In no_header_mode, we can just set the dictionary, since no
// checking is done to advance past header bits to get us in the
// dictionary setting mode. In settings_.gzip_header_mode_ we've already
// removed headers, so this code works too.
if (settings_.dictionary_) {
err = inflateSetDictionary(&uncomp_stream_, settings_.dictionary_,
settings_.dict_len_);
if (err != Z_OK) {
LogWarning("inflateSetDictionary: Error: %d dict_len: %d", err,
settings_.dict_len_);
UncompressErrorInit();
return err;
}
init_settings_.dictionary_ = settings_.dictionary_;
init_settings_.dict_len_ = settings_.dict_len_;
}
}
first_chunk_ = false; // so we don't do this again
// For the first chunk *only* (to avoid infinite troubles), we let
// there be no actual data to uncompress. This sometimes triggers
// when the input is only the gzip header, say.
if (*sourceLen == 0) {
*destLen = 0;
return Z_OK;
}
}
// We'll uncompress as much as we can. If we end OK great, otherwise
// if we get an error that seems to be the gzip footer, we store the
// gzip footer and return OK, otherwise we return the error.
// flush_mode is Z_SYNC_FLUSH for chunked mode, Z_FINISH for all mode.
err = inflate(&uncomp_stream_, flush_mode);
if (settings_.dictionary_ && err == Z_NEED_DICT) {
err = inflateSetDictionary(&uncomp_stream_, settings_.dictionary_,
settings_.dict_len_);
if (err != Z_OK) {
LogWarning("UncompressChunkOrAll: failed in inflateSetDictionary :%d ",
err);
UncompressErrorInit();
return err;
}
init_settings_.dictionary_ = settings_.dictionary_;
init_settings_.dict_len_ = settings_.dict_len_;
err = inflate(&uncomp_stream_, flush_mode);
}
// Figure out how many bytes of the input zlib slurped up:
const uLong bytes_read = uncomp_stream_.total_in - old_total_in;
FIREBASE_ASSERT(source + bytes_read <= source + *sourceLen);
*sourceLen = uncomp_stream_.avail_in;
// Next we look at the footer, if any. Note that we might currently
// have just part of the footer (eg, if this data is arriving over a
// socket). After looking for a footer, log a warning if there is
// extra cruft.
if ((err == Z_STREAM_END) &&
((gzip_footer_bytes_ == -1) ||
(gzip_footer_bytes_ < sizeof(gzip_footer_))) &&
(uncomp_stream_.avail_in <= sizeof(gzip_footer_) ||
// When this flag is true, we allow some extra bytes after the
// zlib footer.
should_be_flexible_with_gzip_footer_)) {
// Due to a bug in old versions of zlibwrapper, we appended the gzip
// footer even in non-gzip mode. Thus we always allow a gzip footer
// even if we're not in gzip mode, so we can continue to uncompress
// the old data. :-(
// Store gzip footer bytes so we can check for footer consistency
// in UncompressChunkDone(). (If we have the whole footer, we
// could do the checking here, but we don't to keep consistency
// with CompressChunkDone().)
gzip_footer_bytes_ = std::min(static_cast<size_t>(uncomp_stream_.avail_in),
sizeof(gzip_footer_));
memcpy(gzip_footer_, source + bytes_read, gzip_footer_bytes_);
} else if ((err == Z_STREAM_END || err == Z_OK) // everything went ok
&& uncomp_stream_.avail_in == 0) { // and we read it all
;
} else if (err == Z_STREAM_END && uncomp_stream_.avail_in > 0) {
LogDebug(
"UncompressChunkOrAll: Received some extra data, bytes total: %d "
"bytes: %s",
uncomp_stream_.avail_in,
reinterpret_cast<const char*>(uncomp_stream_.next_in));
UncompressErrorInit();
return Z_DATA_ERROR; // what's the extra data for?
} else if (err != Z_OK && err != Z_STREAM_END && err != Z_BUF_ERROR) {
// an error happened
LogDebug("UncompressChunkOrAll: Error: %d avail_out: %d", err,
uncomp_stream_.avail_out);
UncompressErrorInit();
return err;
} else if (uncomp_stream_.avail_out == 0) {
err = Z_BUF_ERROR;
}
assert(err == Z_OK || err == Z_BUF_ERROR || err == Z_STREAM_END);
if (err == Z_STREAM_END && !settings_.dont_hide_zstream_end_) err = Z_OK;
// update the crc and other metadata
uncompressed_size_ = uncomp_stream_.total_out;
*destLen = uncomp_stream_.total_out - old_total_out; // size for this call
if (settings_.gzip_header_mode_) crc_ = crc32(crc_, dest, *destLen);
return err;
}