Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(254)

Unified Diff: net/filter/gzip_stream_source_unittest.cc

Issue 1662763002: [ON HOLD] Implement pull-based design for content decoding (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/filter/gzip_stream_source_unittest.cc
diff --git a/net/filter/gzip_stream_source_unittest.cc b/net/filter/gzip_stream_source_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2249412291a91b9b054c71e525c022d8873f5af5
--- /dev/null
+++ b/net/filter/gzip_stream_source_unittest.cc
@@ -0,0 +1,309 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/bit_cast.h"
+#include "base/callback.h"
+#include "net/base/io_buffer.h"
+#include "net/filter/gzip_stream_source.h"
+#include "net/filter/mock_stream_source.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/zlib/zlib.h"
+
+namespace {
+
+using net::Error;
+using net::GzipStreamSource;
+using net::MockStreamSource;
+
+const size_t kBufferSize = 4096;
+
+// How many bytes to leave unused at the end of |source_data_|. This margin is
+// present so that tests that need to append data after the zlib EOF do not run
+// out of room in the output buffer.
+const size_t kEOFMargin = 64;
+
+class GzipStreamSourceTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ::testing::Test::SetUp();
+
+ source_data_ = new char[kBufferSize];
+ source_data_len_ = kBufferSize - kEOFMargin;
+
+ for (size_t i = 0; i < source_data_len_; i++)
+ source_data_[i] = i % 256;
+
+ deflated_data_ = new char[kBufferSize];
+ deflated_data_len_ = kBufferSize;
+
+ Compress(source_data_, source_data_len_, deflated_data_,
+ &deflated_data_len_, false);
+
+ gzipped_data_ = new char[kBufferSize];
+ gzipped_data_len_ = kBufferSize;
+
+ Compress(source_data_, source_data_len_, gzipped_data_, &gzipped_data_len_,
+ true);
+
+ out_buffer_ = new net::IOBuffer(kBufferSize);
+
+ last_error_ = net::OK;
+ last_bytes_read_ = 0;
+
+ scoped_ptr<MockStreamSource> deflate_source(new MockStreamSource);
+ deflate_source_ = deflate_source.get();
+ deflate_stream_.reset(new GzipStreamSource(std::move(deflate_source)));
+ deflate_stream_->Init(GzipStreamSource::GZIP_STREAM_SOURCE_DEFLATE, false);
+
+ scoped_ptr<MockStreamSource> gzip_source(new MockStreamSource);
+ gzip_source_ = gzip_source.get();
+ gzip_stream_.reset(new GzipStreamSource(std::move(gzip_source)));
+ gzip_stream_->Init(GzipStreamSource::GZIP_STREAM_SOURCE_GZIP, true);
+ }
+
+ void Compress(char* source,
+ size_t source_len,
+ char* dest,
+ size_t* dest_len,
+ bool gzip_framing) {
+ size_t dest_left = *dest_len;
+ z_stream zlib_stream;
+ memset(&zlib_stream, 0, sizeof(zlib_stream));
+ int code;
+ if (gzip_framing) {
+ const int kMemLevel = 8; // the default, see deflateInit2(3)
+ code = deflateInit2(&zlib_stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
+ -MAX_WBITS, kMemLevel, Z_DEFAULT_STRATEGY);
+ } else {
+ code = deflateInit(&zlib_stream, Z_DEFAULT_COMPRESSION);
+ }
+ DCHECK_EQ(Z_OK, code);
+
+ // If compressing with gzip framing, prepend a gzip header. See RFC 1952 2.2
+ // and 2.3 for more information.
+ if (gzip_framing) {
+ static const char gzip_header[] = {
+ 0x1f,
+ 0x8b, // magic number
+ 0x08, // CM 0x08 == "deflate"
+ 0x00, // FLG 0x00 == nothing
+ 0x00, 0x00, 0x00,
+ 0x00, // MTIME 0x00000000 == no mtime
+ 0x00, // XFL 0x00 == nothing
+ 0xff, // OS 0xff == unknown
+ };
+ DCHECK_GE(dest_left, sizeof(gzip_header));
+ memcpy(dest, gzip_header, sizeof(gzip_header));
+ dest += sizeof(gzip_header);
+ dest_left -= sizeof(gzip_header);
+ }
+
+ zlib_stream.next_in = bit_cast<Bytef*>(source);
+ zlib_stream.avail_in = source_len;
+ zlib_stream.next_out = bit_cast<Bytef*>(dest);
+ zlib_stream.avail_out = dest_left;
+
+ code = deflate(&zlib_stream, Z_FINISH);
+ DCHECK_EQ(Z_STREAM_END, code);
+ dest_left = zlib_stream.avail_out;
+
+ deflateEnd(&zlib_stream);
+ *dest_len -= dest_left;
+ }
+
+ void OnReadComplete(Error error, size_t bytes_read) {
+ last_error_ = error;
+ last_bytes_read_ = bytes_read;
+ }
+
+ char* source_data() { return source_data_; }
+ size_t source_data_len() { return source_data_len_; }
+
+ char* deflated_data() { return deflated_data_; }
+ size_t deflated_data_len() { return deflated_data_len_; }
+
+ char* gzipped_data() { return gzipped_data_; }
+ size_t gzipped_data_len() { return gzipped_data_len_; }
+
+ net::IOBuffer* out_buffer() { return out_buffer_.get(); }
+ char* out_data() { return out_buffer_->data(); }
+ size_t out_buffer_size() { return kBufferSize; }
+
+ Error last_error() { return last_error_; }
+ size_t last_bytes_read() { return last_bytes_read_; }
+
+ MockStreamSource* deflate_source() { return deflate_source_; }
+ MockStreamSource* gzip_source() { return gzip_source_; }
+
+ net::StreamSource::OnReadCompleteCallback callback() {
+ return base::Bind(&GzipStreamSourceTest::OnReadComplete,
+ base::Unretained(this));
+ }
+
+ void AddTrailingDeflatedData(const char* data, size_t sz) {
+ DCHECK_LE(sz, kBufferSize - deflated_data_len_);
+ memcpy(deflated_data_ + deflated_data_len_, data, sz);
+ deflated_data_len_ += sz;
+ }
+
+ void ReadStream(GzipStreamSource* stream) {
+ last_error_ = stream->Read(out_buffer(), out_buffer_size(),
+ &last_bytes_read_, callback());
+ }
+
+ void ReadDeflateStream() { return ReadStream(deflate_stream_.get()); }
+
+ void ReadGzipStream() { return ReadStream(gzip_stream_.get()); }
+
+ void ReadStreamAll(GzipStreamSource* stream) {
+ char buffer[kBufferSize];
+ size_t index = 0;
+ do {
+ ReadStream(stream);
+ ASSERT_LE(last_bytes_read_, kBufferSize - index);
+ memcpy(buffer + index, out_data(), last_bytes_read_);
+ index += last_bytes_read_;
+ } while (last_error_ == net::OK && last_bytes_read_ > 0);
+ memcpy(out_data(), buffer, index);
+ if (last_error_ == net::OK)
+ last_bytes_read_ = index;
+ }
+
+ void ReadDeflateStreamAll() { ReadStreamAll(deflate_stream_.get()); }
+
+ void ReadGzipStreamAll() { ReadStreamAll(gzip_stream_.get()); }
+
+ private:
+ char* source_data_;
+ size_t source_data_len_;
+
+ char* deflated_data_;
+ size_t deflated_data_len_;
+
+ char* gzipped_data_;
+ size_t gzipped_data_len_;
+
+ scoped_refptr<net::IOBuffer> out_buffer_;
+
+ MockStreamSource* deflate_source_;
+ scoped_ptr<GzipStreamSource> deflate_stream_;
+ MockStreamSource* gzip_source_;
+ scoped_ptr<GzipStreamSource> gzip_stream_;
+
+ Error last_error_;
+ size_t last_bytes_read_;
+};
+
+TEST_F(GzipStreamSourceTest, EmptyStream) {
+ deflate_source()->AddReadResult("", 0, net::OK, true);
+ ReadDeflateStream();
+ EXPECT_EQ(net::OK, last_error());
+ EXPECT_EQ(0U, last_bytes_read());
+}
+
+TEST_F(GzipStreamSourceTest, DeflateOneBlockSync) {
+ deflate_source()->AddReadResult(deflated_data(), deflated_data_len(), net::OK,
+ true);
+ ReadDeflateStream();
+ EXPECT_EQ(net::OK, last_error());
+ EXPECT_EQ(last_bytes_read(), source_data_len());
+ EXPECT_EQ(0, memcmp(out_data(), source_data(), source_data_len()));
+}
+
+TEST_F(GzipStreamSourceTest, GzipOneBlockSync) {
+ gzip_source()->AddReadResult(gzipped_data(), gzipped_data_len(), net::OK,
+ true);
+ ReadGzipStream();
+ EXPECT_EQ(net::OK, last_error());
+ EXPECT_EQ(last_bytes_read(), source_data_len());
+ EXPECT_EQ(0, memcmp(out_data(), source_data(), source_data_len()));
+}
+
+TEST_F(GzipStreamSourceTest, DeflateTwoBlocksSync) {
+ deflate_source()->AddReadResult(deflated_data(), 10, net::OK, true);
+ deflate_source()->AddReadResult(deflated_data() + 10,
+ deflated_data_len() - 10, net::OK, true);
+ ReadDeflateStreamAll();
+ EXPECT_EQ(net::OK, last_error());
+ EXPECT_EQ(last_bytes_read(), source_data_len());
+ EXPECT_EQ(0, memcmp(out_data(), source_data(), source_data_len()));
+}
+
+TEST_F(GzipStreamSourceTest, DeflateOneBlockAsync) {
+ deflate_source()->AddReadResult(deflated_data(), deflated_data_len(), net::OK,
+ false);
+ ReadDeflateStream();
+ EXPECT_EQ(net::ERR_IO_PENDING, last_error());
+ deflate_source()->CompleteNextRead();
+ EXPECT_EQ(net::OK, last_error());
+ EXPECT_EQ(last_bytes_read(), source_data_len());
+ EXPECT_EQ(0, memcmp(out_data(), source_data(), source_data_len()));
+}
+
+TEST_F(GzipStreamSourceTest, PassThroughAfterEOF) {
+ char test_data[] = "Hello, World!";
+ AddTrailingDeflatedData(test_data, sizeof(test_data));
+ deflate_source()->AddReadResult(deflated_data(), deflated_data_len(), net::OK,
+ true);
+ // Compressed and uncompressed data get returned as separate Read() results,
+ // so this test has to use ReadDeflateStreamAll to get both.
+ ReadDeflateStreamAll();
+ EXPECT_EQ(net::OK, last_error());
+ EXPECT_EQ(last_bytes_read(), source_data_len() + sizeof(test_data));
+ EXPECT_EQ(0, memcmp(out_data(), source_data(), source_data_len()));
+ EXPECT_EQ(
+ 0, memcmp(out_data() + source_data_len(), test_data, sizeof(test_data)));
+}
+
+TEST_F(GzipStreamSourceTest, MissingZlibHeader) {
+ const size_t kZlibHeaderLen = 2;
+ deflate_source()->AddReadResult(deflated_data() + kZlibHeaderLen,
+ deflated_data_len() - kZlibHeaderLen, net::OK,
+ true);
+ ReadDeflateStream();
+ EXPECT_EQ(net::OK, last_error());
+ EXPECT_EQ(last_bytes_read(), source_data_len());
+ EXPECT_EQ(0, memcmp(out_data(), source_data(), source_data_len()));
+}
+
+// A slight wrinkle: since the test GZip stream runs with gzip_fallback set to
+// true, corrupting the first byte instead of the second byte will cause the
+// gzip stream to falsely accept the corrupt data and pass it through untouched.
+TEST_F(GzipStreamSourceTest, CorruptGzipHeader) {
+ gzipped_data()[1] = 0;
+ gzip_source()->AddReadResult(gzipped_data(), gzipped_data_len(), net::OK,
+ true);
+ ReadGzipStream();
+ EXPECT_EQ(net::ERR_CONTENT_DECODING_FAILED, last_error());
+}
+
+TEST_F(GzipStreamSourceTest, GzipFallback) {
+ gzip_source()->AddReadResult(source_data(), source_data_len(), net::OK, true);
+ ReadGzipStream();
+ EXPECT_EQ(net::OK, last_error());
+ EXPECT_EQ(last_bytes_read(), source_data_len());
+ EXPECT_EQ(0, memcmp(out_data(), source_data(), source_data_len()));
+}
+
+// This test checks that the gzip stream source works correctly on 'golden' data
+// as produced by gzip(1).
+TEST_F(GzipStreamSourceTest, GzipCorrectness) {
+ char plain_data[] = "Hello, World!";
+ char gzip_data[] = {
+ // From:
+ // echo -n 'Hello, World!' | gzip | xxd -i | sed -e 's/^/ /'
+ 0x1f, 0x8b, 0x08, 0x00, 0x2b, 0x02, 0x84, 0x55, 0x00, 0x03, 0xf3,
+ 0x48, 0xcd, 0xc9, 0xc9, 0xd7, 0x51, 0x08, 0xcf, 0x2f, 0xca, 0x49,
+ 0x51, 0x04, 0x00, 0xd0, 0xc3, 0x4a, 0xec, 0x0d, 0x00, 0x00, 0x00};
+ gzip_source()->AddReadResult(gzip_data, sizeof(gzip_data), net::OK, true);
+ ReadGzipStream();
+ EXPECT_EQ(net::OK, last_error());
+ // Using strlen(3) here because echo doesn't leave the null terminator on the
+ // string.
+ EXPECT_EQ(strlen(plain_data), last_bytes_read());
+ EXPECT_EQ(0, memcmp(out_data(), plain_data, strlen(plain_data)));
+}
+
+} // namespace

Powered by Google App Engine
This is Rietveld 408576698