This source file includes following definitions.
- possible_sdch_pass_through_
- InitDecoding
- ReadFilteredData
- CheckGZipHeader
- DoInflate
- InsertZlibHeader
- SkipGZipFooter
#include "net/filter/gzip_filter.h"
#include "base/logging.h"
#include "net/filter/gzip_header.h"
#include "third_party/zlib/zlib.h"
namespace net {
GZipFilter::GZipFilter()
: decoding_status_(DECODING_UNINITIALIZED),
decoding_mode_(DECODE_MODE_UNKNOWN),
gzip_header_status_(GZIP_CHECK_HEADER_IN_PROGRESS),
zlib_header_added_(false),
gzip_footer_bytes_(0),
possible_sdch_pass_through_(false) {
}
GZipFilter::~GZipFilter() {
if (decoding_status_ != DECODING_UNINITIALIZED) {
inflateEnd(zlib_stream_.get());
}
}
bool GZipFilter::InitDecoding(Filter::FilterType filter_type) {
if (decoding_status_ != DECODING_UNINITIALIZED)
return false;
zlib_stream_.reset(new z_stream);
if (!zlib_stream_.get())
return false;
memset(zlib_stream_.get(), 0, sizeof(z_stream));
switch (filter_type) {
case Filter::FILTER_TYPE_DEFLATE: {
if (inflateInit(zlib_stream_.get()) != Z_OK)
return false;
decoding_mode_ = DECODE_MODE_DEFLATE;
break;
}
case Filter::FILTER_TYPE_GZIP_HELPING_SDCH:
possible_sdch_pass_through_ = true;
case Filter::FILTER_TYPE_GZIP: {
gzip_header_.reset(new GZipHeader());
if (!gzip_header_.get())
return false;
if (inflateInit2(zlib_stream_.get(), -MAX_WBITS) != Z_OK)
return false;
decoding_mode_ = DECODE_MODE_GZIP;
break;
}
default: {
return false;
}
}
decoding_status_ = DECODING_IN_PROGRESS;
return true;
}
Filter::FilterStatus GZipFilter::ReadFilteredData(char* dest_buffer,
int* dest_len) {
if (!dest_buffer || !dest_len || *dest_len <= 0)
return Filter::FILTER_ERROR;
if (decoding_status_ == DECODING_DONE) {
if (GZIP_GET_INVALID_HEADER != gzip_header_status_)
SkipGZipFooter();
return CopyOut(dest_buffer, dest_len);
}
if (decoding_status_ != DECODING_IN_PROGRESS)
return Filter::FILTER_ERROR;
Filter::FilterStatus status;
if (decoding_mode_ == DECODE_MODE_GZIP &&
gzip_header_status_ == GZIP_CHECK_HEADER_IN_PROGRESS) {
status = CheckGZipHeader();
switch (status) {
case Filter::FILTER_NEED_MORE_DATA: {
*dest_len = 0;
possible_sdch_pass_through_ = false;
return status;
}
case Filter::FILTER_OK: {
DCHECK_EQ(gzip_header_status_, GZIP_GET_COMPLETE_HEADER);
break;
}
case Filter::FILTER_ERROR: {
if (possible_sdch_pass_through_ &&
GZIP_GET_INVALID_HEADER == gzip_header_status_) {
decoding_status_ = DECODING_DONE;
return CopyOut(dest_buffer, dest_len);
}
decoding_status_ = DECODING_ERROR;
return status;
}
default: {
status = Filter::FILTER_ERROR;
decoding_status_ = DECODING_ERROR;
return status;
}
}
}
int dest_orig_size = *dest_len;
status = DoInflate(dest_buffer, dest_len);
if (decoding_mode_ == DECODE_MODE_DEFLATE && status == Filter::FILTER_ERROR) {
if (InsertZlibHeader()) {
*dest_len = dest_orig_size;
status = DoInflate(dest_buffer, dest_len);
}
}
if (status == Filter::FILTER_DONE) {
decoding_status_ = DECODING_DONE;
} else if (status == Filter::FILTER_ERROR) {
decoding_status_ = DECODING_ERROR;
}
return status;
}
Filter::FilterStatus GZipFilter::CheckGZipHeader() {
DCHECK_EQ(gzip_header_status_, GZIP_CHECK_HEADER_IN_PROGRESS);
if (!next_stream_data_ || stream_data_len_ <= 0)
return Filter::FILTER_ERROR;
const char* header_end = NULL;
GZipHeader::Status header_status;
header_status = gzip_header_->ReadMore(next_stream_data_, stream_data_len_,
&header_end);
switch (header_status) {
case GZipHeader::INCOMPLETE_HEADER: {
next_stream_data_ = NULL;
stream_data_len_ = 0;
return Filter::FILTER_NEED_MORE_DATA;
}
case GZipHeader::COMPLETE_HEADER: {
int num_chars_left = static_cast<int>(stream_data_len_ -
(header_end - next_stream_data_));
gzip_header_status_ = GZIP_GET_COMPLETE_HEADER;
if (num_chars_left > 0) {
next_stream_data_ = const_cast<char*>(header_end);
stream_data_len_ = num_chars_left;
return Filter::FILTER_OK;
} else {
next_stream_data_ = NULL;
stream_data_len_ = 0;
return Filter::FILTER_NEED_MORE_DATA;
}
}
case GZipHeader::INVALID_HEADER: {
gzip_header_status_ = GZIP_GET_INVALID_HEADER;
return Filter::FILTER_ERROR;
}
default: {
break;
}
}
return Filter::FILTER_ERROR;
}
Filter::FilterStatus GZipFilter::DoInflate(char* dest_buffer, int* dest_len) {
if (!dest_buffer || !dest_len || *dest_len <= 0)
return Filter::FILTER_ERROR;
if (!next_stream_data_ || stream_data_len_ <= 0) {
*dest_len = 0;
return Filter::FILTER_NEED_MORE_DATA;
}
zlib_stream_.get()->next_in = bit_cast<Bytef*>(next_stream_data_);
zlib_stream_.get()->avail_in = stream_data_len_;
zlib_stream_.get()->next_out = bit_cast<Bytef*>(dest_buffer);
zlib_stream_.get()->avail_out = *dest_len;
int inflate_code = inflate(zlib_stream_.get(), Z_NO_FLUSH);
int bytesWritten = *dest_len - zlib_stream_.get()->avail_out;
Filter::FilterStatus status;
switch (inflate_code) {
case Z_STREAM_END: {
*dest_len = bytesWritten;
stream_data_len_ = zlib_stream_.get()->avail_in;
next_stream_data_ = bit_cast<char*>(zlib_stream_.get()->next_in);
SkipGZipFooter();
status = Filter::FILTER_DONE;
break;
}
case Z_BUF_ERROR: {
status = Filter::FILTER_ERROR;
break;
}
case Z_OK: {
*dest_len = bytesWritten;
stream_data_len_ = zlib_stream_.get()->avail_in;
if (stream_data_len_ == 0) {
next_stream_data_ = NULL;
status = Filter::FILTER_NEED_MORE_DATA;
} else {
next_stream_data_ = bit_cast<char*>(zlib_stream_.get()->next_in);
status = Filter::FILTER_OK;
}
break;
}
default: {
status = Filter::FILTER_ERROR;
break;
}
}
return status;
}
bool GZipFilter::InsertZlibHeader() {
static char dummy_head[2] = { 0x78, 0x1 };
char dummy_output[4];
if (zlib_header_added_)
return false;
inflateReset(zlib_stream_.get());
zlib_stream_.get()->next_in = bit_cast<Bytef*>(&dummy_head[0]);
zlib_stream_.get()->avail_in = sizeof(dummy_head);
zlib_stream_.get()->next_out = bit_cast<Bytef*>(&dummy_output[0]);
zlib_stream_.get()->avail_out = sizeof(dummy_output);
int code = inflate(zlib_stream_.get(), Z_NO_FLUSH);
zlib_header_added_ = true;
return (code == Z_OK);
}
void GZipFilter::SkipGZipFooter() {
int footer_bytes_expected = kGZipFooterSize - gzip_footer_bytes_;
if (footer_bytes_expected > 0) {
int footer_byte_avail = std::min(footer_bytes_expected, stream_data_len_);
stream_data_len_ -= footer_byte_avail;
next_stream_data_ += footer_byte_avail;
gzip_footer_bytes_ += footer_byte_avail;
if (stream_data_len_ == 0)
next_stream_data_ = NULL;
}
}
}