blob: ea64039fa50d11cea1379dd591900b6f43775246 [file] [log] [blame]
// 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 <string>
#include "base/bind.h"
#include "base/bit_cast.h"
#include "base/callback.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "net/base/io_buffer.h"
#include "net/base/test_completion_callback.h"
#include "net/filter/brotli_source_stream.h"
#include "net/filter/mock_source_stream.h"
#include "starboard/memory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
namespace net {
namespace {
const size_t kDefaultBufferSize = 4096;
const size_t kSmallBufferSize = 128;
} // namespace
class BrotliSourceStreamTest : public PlatformTest {
protected:
void SetUp() override {
PlatformTest::SetUp();
// Get the path of data directory.
base::FilePath data_dir;
base::PathService::Get(base::DIR_TEST_DATA, &data_dir);
data_dir = data_dir.AppendASCII("net");
data_dir = data_dir.AppendASCII("data");
data_dir = data_dir.AppendASCII("filter_unittests");
// Read data from the original file into buffer.
base::FilePath file_path;
file_path = data_dir.AppendASCII("google.txt");
ASSERT_TRUE(base::ReadFileToString(file_path, &source_data_));
ASSERT_GE(kDefaultBufferSize, source_data_.size());
// Read data from the encoded file into buffer.
base::FilePath encoded_file_path;
encoded_file_path = data_dir.AppendASCII("google.br");
ASSERT_TRUE(base::ReadFileToString(encoded_file_path, &encoded_buffer_));
ASSERT_GE(kDefaultBufferSize, encoded_buffer_.size());
std::unique_ptr<MockSourceStream> source(new MockSourceStream);
source_ = source.get();
brotli_stream_ = CreateBrotliSourceStream(std::move(source));
}
int ReadStream(const TestCompletionCallback& callback) {
return brotli_stream_->Read(out_buffer(), out_data_size(),
callback.callback());
}
IOBuffer* out_buffer() { return out_buffer_.get(); }
char* out_data() { return out_buffer_->data(); }
size_t out_data_size() { return out_buffer_->size(); }
std::string source_data() { return source_data_; }
size_t source_data_len() { return source_data_.length(); }
char* encoded_buffer() { return &encoded_buffer_[0]; }
size_t encoded_len() { return encoded_buffer_.length(); }
MockSourceStream* source() { return source_; }
SourceStream* brotli_stream() { return brotli_stream_.get(); }
scoped_refptr<IOBufferWithSize> out_buffer_;
private:
MockSourceStream* source_;
std::unique_ptr<SourceStream> brotli_stream_;
std::unique_ptr<base::RunLoop> loop_;
std::string source_data_;
std::string encoded_buffer_;
};
// Basic scenario: decoding brotli data with big enough buffer.
TEST_F(BrotliSourceStreamTest, DecodeBrotliOneBlockSync) {
source()->AddReadResult(encoded_buffer(), encoded_len(), OK,
MockSourceStream::SYNC);
out_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kDefaultBufferSize);
TestCompletionCallback callback;
int bytes_read = ReadStream(callback);
EXPECT_EQ(static_cast<int>(source_data_len()), bytes_read);
EXPECT_EQ(
0, SbMemoryCompare(out_data(), source_data().c_str(), source_data_len()));
EXPECT_EQ("BROTLI", brotli_stream()->Description());
}
// Regression test for crbug.com/659311. The following example is taken out
// of the bug report. For this specific example, Brotli will consume the first
// byte in the 6 available bytes and return 0.
TEST_F(BrotliSourceStreamTest, IgnoreExtraData) {
const unsigned char kResponse[] = {0x1A, 0xDF, 0x6E, 0x74, 0x74, 0x68};
source()->AddReadResult(reinterpret_cast<const char*>(kResponse),
sizeof(kResponse), OK, MockSourceStream::SYNC);
// Add an EOF.
source()->AddReadResult(reinterpret_cast<const char*>(kResponse), 0, OK,
MockSourceStream::SYNC);
out_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kDefaultBufferSize);
std::string actual_output;
TestCompletionCallback callback;
int bytes_read = ReadStream(callback);
EXPECT_EQ(0, bytes_read);
EXPECT_EQ("BROTLI", brotli_stream()->Description());
}
// If there are data after decoding is done, ignore the data. crbug.com/659311.
TEST_F(BrotliSourceStreamTest, IgnoreExtraDataInOneRead) {
std::string response_with_extra_data(encoded_buffer(), encoded_len());
response_with_extra_data.append(1000, 'x');
source()->AddReadResult(response_with_extra_data.c_str(),
response_with_extra_data.length(), OK,
MockSourceStream::SYNC);
// Add an EOF.
source()->AddReadResult(response_with_extra_data.c_str(), 0, OK,
MockSourceStream::SYNC);
out_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kDefaultBufferSize);
std::string actual_output;
TestCompletionCallback callback;
while (true) {
int bytes_read = ReadStream(callback);
if (bytes_read == OK)
break;
ASSERT_GT(bytes_read, OK);
actual_output.append(out_data(), bytes_read);
}
EXPECT_EQ(source_data_len(), actual_output.size());
EXPECT_EQ(source_data(), actual_output);
EXPECT_EQ("BROTLI", brotli_stream()->Description());
}
// Same as above but extra data is in a different read.
TEST_F(BrotliSourceStreamTest, IgnoreExtraDataInDifferentRead) {
std::string extra_data;
extra_data.append(1000, 'x');
source()->AddReadResult(encoded_buffer(), encoded_len(), OK,
MockSourceStream::SYNC);
source()->AddReadResult(extra_data.c_str(), extra_data.length(), OK,
MockSourceStream::SYNC);
// Add an EOF.
source()->AddReadResult(extra_data.c_str(), 0, OK, MockSourceStream::SYNC);
out_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kDefaultBufferSize);
std::string actual_output;
TestCompletionCallback callback;
while (true) {
int bytes_read = ReadStream(callback);
if (bytes_read == OK)
break;
ASSERT_GT(bytes_read, OK);
actual_output.append(out_data(), bytes_read);
}
EXPECT_EQ(source_data_len(), actual_output.size());
EXPECT_EQ(source_data(), actual_output);
EXPECT_EQ("BROTLI", brotli_stream()->Description());
}
// Basic scenario: decoding brotli data with big enough buffer.
TEST_F(BrotliSourceStreamTest, DecodeBrotliTwoBlockSync) {
source()->AddReadResult(encoded_buffer(), 10, OK, MockSourceStream::SYNC);
source()->AddReadResult(encoded_buffer() + 10, encoded_len() - 10, OK,
MockSourceStream::SYNC);
out_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kDefaultBufferSize);
TestCompletionCallback callback;
int bytes_read = ReadStream(callback);
EXPECT_EQ(static_cast<int>(source_data_len()), bytes_read);
EXPECT_EQ(
0, SbMemoryCompare(out_data(), source_data().c_str(), source_data_len()));
EXPECT_EQ("BROTLI", brotli_stream()->Description());
}
// Basic scenario: decoding brotli data with big enough buffer.
TEST_F(BrotliSourceStreamTest, DecodeBrotliOneBlockAsync) {
source()->AddReadResult(encoded_buffer(), encoded_len(), OK,
MockSourceStream::ASYNC);
out_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kDefaultBufferSize);
TestCompletionCallback callback;
int bytes_read = ReadStream(callback);
EXPECT_EQ(ERR_IO_PENDING, bytes_read);
source()->CompleteNextRead();
int rv = callback.WaitForResult();
EXPECT_EQ(static_cast<int>(source_data_len()), rv);
EXPECT_EQ(
0, SbMemoryCompare(out_data(), source_data().c_str(), source_data_len()));
EXPECT_EQ("BROTLI", brotli_stream()->Description());
}
// Tests we can call filter repeatedly to get all the data decoded.
// To do that, we create a filter with a small buffer that can not hold all
// the input data.
TEST_F(BrotliSourceStreamTest, DecodeWithSmallBufferSync) {
source()->AddReadResult(encoded_buffer(), encoded_len(), OK,
MockSourceStream::SYNC);
// Add a 0 byte read to signal EOF.
source()->AddReadResult(encoded_buffer(), 0, OK, MockSourceStream::SYNC);
out_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kSmallBufferSize);
scoped_refptr<IOBuffer> buffer =
base::MakeRefCounted<IOBufferWithSize>(source_data_len());
size_t total_bytes_read = 0;
int bytes_read = 0;
TestCompletionCallback callback;
do {
bytes_read = ReadStream(callback);
EXPECT_LE(OK, bytes_read);
EXPECT_GE(kSmallBufferSize, static_cast<size_t>(bytes_read));
SbMemoryCopy(buffer->data() + total_bytes_read, out_data(), bytes_read);
total_bytes_read += bytes_read;
} while (bytes_read > 0);
EXPECT_EQ(source_data_len(), total_bytes_read);
EXPECT_EQ(0, SbMemoryCompare(buffer->data(), source_data().c_str(),
total_bytes_read));
EXPECT_EQ("BROTLI", brotli_stream()->Description());
}
// Tests we can call filter repeatedly to get all the data decoded.
// To do that, we create a filter with a small buffer that can not hold all
// the input data.
TEST_F(BrotliSourceStreamTest, DecodeWithSmallBufferAsync) {
source()->AddReadResult(encoded_buffer(), encoded_len(), OK,
MockSourceStream::ASYNC);
// Add a 0 byte read to signal EOF.
source()->AddReadResult(encoded_buffer(), 0, OK, MockSourceStream::ASYNC);
out_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kSmallBufferSize);
scoped_refptr<IOBuffer> buffer =
base::MakeRefCounted<IOBufferWithSize>(source_data_len());
size_t total_bytes_read = 0;
int bytes_read = 0;
do {
TestCompletionCallback callback;
bytes_read = ReadStream(callback);
if (bytes_read == ERR_IO_PENDING) {
source()->CompleteNextRead();
bytes_read = callback.WaitForResult();
}
EXPECT_GE(static_cast<int>(kSmallBufferSize), bytes_read);
SbMemoryCopy(buffer->data() + total_bytes_read, out_data(), bytes_read);
total_bytes_read += bytes_read;
} while (bytes_read > 0);
EXPECT_EQ(source_data_len(), total_bytes_read);
EXPECT_EQ(0, SbMemoryCompare(buffer->data(), source_data().c_str(),
total_bytes_read));
EXPECT_EQ("BROTLI", brotli_stream()->Description());
}
// Tests we can still decode with just 1 byte buffer in the filter.
// The purpose of this test: sometimes the filter will consume input without
// generating output. Verify filter can handle it correctly.
TEST_F(BrotliSourceStreamTest, DecodeWithOneByteBuffer) {
source()->AddReadResult(encoded_buffer(), encoded_len(), OK,
MockSourceStream::SYNC);
// Add a 0 byte read to signal EOF.
source()->AddReadResult(encoded_buffer(), 0, OK, MockSourceStream::SYNC);
out_buffer_ = base::MakeRefCounted<IOBufferWithSize>(1);
scoped_refptr<IOBuffer> buffer =
base::MakeRefCounted<IOBufferWithSize>(source_data_len());
size_t total_bytes_read = 0;
int bytes_read = 0;
do {
TestCompletionCallback callback;
bytes_read = ReadStream(callback);
EXPECT_NE(ERR_IO_PENDING, bytes_read);
EXPECT_GE(1, bytes_read);
SbMemoryCopy(buffer->data() + total_bytes_read, out_data(), bytes_read);
total_bytes_read += bytes_read;
} while (bytes_read > 0);
EXPECT_EQ(source_data_len(), total_bytes_read);
EXPECT_EQ(0, SbMemoryCompare(buffer->data(), source_data().c_str(),
source_data_len()));
EXPECT_EQ("BROTLI", brotli_stream()->Description());
}
// Decoding deflate stream with corrupted data.
TEST_F(BrotliSourceStreamTest, DecodeCorruptedData) {
char corrupt_data[kDefaultBufferSize];
int corrupt_data_len = encoded_len();
SbMemoryCopy(corrupt_data, encoded_buffer(), encoded_len());
int pos = corrupt_data_len / 2;
corrupt_data[pos] = !corrupt_data[pos];
source()->AddReadResult(corrupt_data, corrupt_data_len, OK,
MockSourceStream::SYNC);
out_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kDefaultBufferSize);
TestCompletionCallback callback;
int error = OK;
do {
error = ReadStream(callback);
EXPECT_NE(ERR_IO_PENDING, error);
} while (error > 0);
// Expect failures
EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, error);
// Calling Read again gives the same error.
error = ReadStream(callback);
EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, error);
EXPECT_EQ("BROTLI", brotli_stream()->Description());
}
// Decoding deflate stream with missing data.
TEST_F(BrotliSourceStreamTest, DecodeMissingData) {
char corrupt_data[kDefaultBufferSize];
int corrupt_data_len = encoded_len();
SbMemoryCopy(corrupt_data, encoded_buffer(), encoded_len());
int pos = corrupt_data_len / 2;
int len = corrupt_data_len - pos - 1;
SbMemoryMove(&corrupt_data[pos], &corrupt_data[pos + 1], len);
--corrupt_data_len;
// Decode the corrupted data with filter
source()->AddReadResult(corrupt_data, corrupt_data_len, OK,
MockSourceStream::SYNC);
out_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kDefaultBufferSize);
int error = OK;
do {
TestCompletionCallback callback;
error = ReadStream(callback);
EXPECT_NE(ERR_IO_PENDING, error);
} while (error > 0);
// Expect failures
EXPECT_EQ(ERR_CONTENT_DECODING_FAILED, error);
EXPECT_EQ("BROTLI", brotli_stream()->Description());
}
// Decoding brotli stream with empty output data.
TEST_F(BrotliSourceStreamTest, DecodeEmptyData) {
char data[1] = {6}; // WBITS = 16, ISLAST = 1, ISLASTEMPTY = 1
int data_len = 1;
source()->AddReadResult(data, data_len, OK, MockSourceStream::SYNC);
source()->AddReadResult(data, 0, OK, MockSourceStream::SYNC);
out_buffer_ = base::MakeRefCounted<IOBufferWithSize>(kDefaultBufferSize);
TestCompletionCallback callback;
int bytes_read = ReadStream(callback);
EXPECT_EQ(OK, bytes_read);
EXPECT_EQ("BROTLI", brotli_stream()->Description());
}
} // namespace net