blob: d05d7beed6e48534f913215651e4779d5fc7ed07 [file]
// 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 "modules/fetch/BytesConsumer.h"
#include "core/testing/DummyPageHolder.h"
#include "modules/fetch/BytesConsumerTestUtil.h"
#include "platform/blob/BlobData.h"
#include "platform/testing/UnitTestHelpers.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "wtf/RefPtr.h"
namespace blink {
namespace {
using Command = BytesConsumerTestUtil::Command;
using Result = BytesConsumer::Result;
using ReplayingBytesConsumer = BytesConsumerTestUtil::ReplayingBytesConsumer;
String toString(const Vector<char>& v) {
return String(v.data(), v.size());
}
class TestClient final : public GarbageCollectedFinalized<TestClient>,
public BytesConsumer::Client {
USING_GARBAGE_COLLECTED_MIXIN(TestClient);
public:
void onStateChange() override { ++m_numOnStateChangeCalled; }
int numOnStateChangeCalled() const { return m_numOnStateChangeCalled; }
private:
int m_numOnStateChangeCalled = 0;
};
class BytesConsumerTeeTest : public ::testing::Test {
public:
BytesConsumerTeeTest() : m_page(DummyPageHolder::create()) {}
Document* document() { return &m_page->document(); }
private:
std::unique_ptr<DummyPageHolder> m_page;
};
class FakeBlobBytesConsumer : public BytesConsumer {
public:
explicit FakeBlobBytesConsumer(PassRefPtr<BlobDataHandle> handle)
: m_blobHandle(handle) {}
~FakeBlobBytesConsumer() override {}
Result beginRead(const char** buffer, size_t* available) override {
if (m_state == PublicState::Closed)
return Result::Done;
m_blobHandle = nullptr;
m_state = PublicState::Errored;
return Result::Error;
}
Result endRead(size_t readSize) override {
if (m_state == PublicState::Closed)
return Result::Error;
m_blobHandle = nullptr;
m_state = PublicState::Errored;
return Result::Error;
}
PassRefPtr<BlobDataHandle> drainAsBlobDataHandle(BlobSizePolicy policy) {
if (m_state != PublicState::ReadableOrWaiting)
return nullptr;
DCHECK(m_blobHandle);
if (policy == BlobSizePolicy::DisallowBlobWithInvalidSize &&
m_blobHandle->size() == UINT64_MAX)
return nullptr;
m_state = PublicState::Closed;
return m_blobHandle.release();
}
void setClient(Client*) override {}
void clearClient() override {}
void cancel() override {}
PublicState getPublicState() const override { return m_state; }
Error getError() const override { return Error(); }
String debugName() const override { return "FakeBlobBytesConsumer"; }
private:
PublicState m_state = PublicState::ReadableOrWaiting;
RefPtr<BlobDataHandle> m_blobHandle;
};
TEST_F(BytesConsumerTeeTest, CreateDone) {
ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
src->add(Command(Command::Done));
EXPECT_FALSE(src->isCancelled());
BytesConsumer* dest1 = nullptr;
BytesConsumer* dest2 = nullptr;
BytesConsumer::tee(document(), src, &dest1, &dest2);
auto result1 = (new BytesConsumerTestUtil::TwoPhaseReader(dest1))->run();
auto result2 = (new BytesConsumerTestUtil::TwoPhaseReader(dest2))->run();
EXPECT_EQ(Result::Done, result1.first);
EXPECT_TRUE(result1.second.isEmpty());
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
EXPECT_EQ(Result::Done, result2.first);
EXPECT_TRUE(result2.second.isEmpty());
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest2->getPublicState());
EXPECT_FALSE(src->isCancelled());
// Cancelling does nothing when closed.
dest1->cancel();
dest2->cancel();
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest2->getPublicState());
EXPECT_FALSE(src->isCancelled());
}
TEST_F(BytesConsumerTeeTest, TwoPhaseRead) {
ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
src->add(Command(Command::Wait));
src->add(Command(Command::Data, "hello, "));
src->add(Command(Command::Wait));
src->add(Command(Command::Data, "world"));
src->add(Command(Command::Wait));
src->add(Command(Command::Wait));
src->add(Command(Command::Done));
BytesConsumer* dest1 = nullptr;
BytesConsumer* dest2 = nullptr;
BytesConsumer::tee(document(), src, &dest1, &dest2);
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest2->getPublicState());
auto result1 = (new BytesConsumerTestUtil::TwoPhaseReader(dest1))->run();
auto result2 = (new BytesConsumerTestUtil::TwoPhaseReader(dest2))->run();
EXPECT_EQ(Result::Done, result1.first);
EXPECT_EQ("hello, world", toString(result1.second));
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
EXPECT_EQ(Result::Done, result2.first);
EXPECT_EQ("hello, world", toString(result2.second));
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest2->getPublicState());
EXPECT_FALSE(src->isCancelled());
}
TEST_F(BytesConsumerTeeTest, Error) {
ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
src->add(Command(Command::Data, "hello, "));
src->add(Command(Command::Data, "world"));
src->add(Command(Command::Error));
BytesConsumer* dest1 = nullptr;
BytesConsumer* dest2 = nullptr;
BytesConsumer::tee(document(), src, &dest1, &dest2);
EXPECT_EQ(BytesConsumer::PublicState::Errored, dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::Errored, dest2->getPublicState());
auto result1 = (new BytesConsumerTestUtil::TwoPhaseReader(dest1))->run();
auto result2 = (new BytesConsumerTestUtil::TwoPhaseReader(dest2))->run();
EXPECT_EQ(Result::Error, result1.first);
EXPECT_TRUE(result1.second.isEmpty());
EXPECT_EQ(BytesConsumer::PublicState::Errored, dest1->getPublicState());
EXPECT_EQ(Result::Error, result2.first);
EXPECT_TRUE(result2.second.isEmpty());
EXPECT_EQ(BytesConsumer::PublicState::Errored, dest2->getPublicState());
EXPECT_FALSE(src->isCancelled());
// Cancelling does nothing when errored.
dest1->cancel();
dest2->cancel();
EXPECT_EQ(BytesConsumer::PublicState::Errored, dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::Errored, dest2->getPublicState());
EXPECT_FALSE(src->isCancelled());
}
TEST_F(BytesConsumerTeeTest, Cancel) {
ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
src->add(Command(Command::Data, "hello, "));
src->add(Command(Command::Wait));
BytesConsumer* dest1 = nullptr;
BytesConsumer* dest2 = nullptr;
BytesConsumer::tee(document(), src, &dest1, &dest2);
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest2->getPublicState());
EXPECT_FALSE(src->isCancelled());
dest1->cancel();
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest2->getPublicState());
EXPECT_FALSE(src->isCancelled());
dest2->cancel();
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest2->getPublicState());
EXPECT_TRUE(src->isCancelled());
}
TEST_F(BytesConsumerTeeTest, CancelShouldNotAffectTheOtherDestination) {
ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
src->add(Command(Command::Data, "hello, "));
src->add(Command(Command::Wait));
src->add(Command(Command::Data, "world"));
src->add(Command(Command::Done));
BytesConsumer* dest1 = nullptr;
BytesConsumer* dest2 = nullptr;
BytesConsumer::tee(document(), src, &dest1, &dest2);
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest2->getPublicState());
EXPECT_FALSE(src->isCancelled());
dest1->cancel();
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest2->getPublicState());
EXPECT_FALSE(src->isCancelled());
auto result2 = (new BytesConsumerTestUtil::TwoPhaseReader(dest2))->run();
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest2->getPublicState());
EXPECT_EQ(Result::Done, result2.first);
EXPECT_EQ("hello, world", toString(result2.second));
EXPECT_FALSE(src->isCancelled());
}
TEST_F(BytesConsumerTeeTest, CancelShouldNotAffectTheOtherDestination2) {
ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
src->add(Command(Command::Data, "hello, "));
src->add(Command(Command::Wait));
src->add(Command(Command::Data, "world"));
src->add(Command(Command::Error));
BytesConsumer* dest1 = nullptr;
BytesConsumer* dest2 = nullptr;
BytesConsumer::tee(document(), src, &dest1, &dest2);
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest2->getPublicState());
EXPECT_FALSE(src->isCancelled());
dest1->cancel();
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest2->getPublicState());
EXPECT_FALSE(src->isCancelled());
auto result2 = (new BytesConsumerTestUtil::TwoPhaseReader(dest2))->run();
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
EXPECT_EQ(BytesConsumer::PublicState::Errored, dest2->getPublicState());
EXPECT_EQ(Result::Error, result2.first);
EXPECT_FALSE(src->isCancelled());
}
TEST_F(BytesConsumerTeeTest, BlobHandle) {
RefPtr<BlobDataHandle> blobDataHandle =
BlobDataHandle::create(BlobData::create(), 12345);
BytesConsumer* src = new FakeBlobBytesConsumer(blobDataHandle);
BytesConsumer* dest1 = nullptr;
BytesConsumer* dest2 = nullptr;
BytesConsumer::tee(document(), src, &dest1, &dest2);
RefPtr<BlobDataHandle> destBlobDataHandle1 = dest1->drainAsBlobDataHandle(
BytesConsumer::BlobSizePolicy::AllowBlobWithInvalidSize);
RefPtr<BlobDataHandle> destBlobDataHandle2 = dest2->drainAsBlobDataHandle(
BytesConsumer::BlobSizePolicy::DisallowBlobWithInvalidSize);
ASSERT_TRUE(destBlobDataHandle1);
ASSERT_TRUE(destBlobDataHandle2);
EXPECT_EQ(12345u, destBlobDataHandle1->size());
EXPECT_EQ(12345u, destBlobDataHandle2->size());
}
TEST_F(BytesConsumerTeeTest, BlobHandleWithInvalidSize) {
RefPtr<BlobDataHandle> blobDataHandle =
BlobDataHandle::create(BlobData::create(), -1);
BytesConsumer* src = new FakeBlobBytesConsumer(blobDataHandle);
BytesConsumer* dest1 = nullptr;
BytesConsumer* dest2 = nullptr;
BytesConsumer::tee(document(), src, &dest1, &dest2);
RefPtr<BlobDataHandle> destBlobDataHandle1 = dest1->drainAsBlobDataHandle(
BytesConsumer::BlobSizePolicy::AllowBlobWithInvalidSize);
RefPtr<BlobDataHandle> destBlobDataHandle2 = dest2->drainAsBlobDataHandle(
BytesConsumer::BlobSizePolicy::DisallowBlobWithInvalidSize);
ASSERT_TRUE(destBlobDataHandle1);
ASSERT_FALSE(destBlobDataHandle2);
EXPECT_EQ(UINT64_MAX, destBlobDataHandle1->size());
}
TEST_F(BytesConsumerTeeTest, ConsumerCanBeErroredInTwoPhaseRead) {
ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
src->add(Command(Command::Data, "a"));
src->add(Command(Command::Wait));
src->add(Command(Command::Error));
BytesConsumer* dest1 = nullptr;
BytesConsumer* dest2 = nullptr;
BytesConsumer::tee(document(), src, &dest1, &dest2);
TestClient* client = new TestClient();
dest1->setClient(client);
const char* buffer = nullptr;
size_t available = 0;
ASSERT_EQ(Result::Ok, dest1->beginRead(&buffer, &available));
ASSERT_EQ(1u, available);
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest1->getPublicState());
int numOnStateChangeCalled = client->numOnStateChangeCalled();
EXPECT_EQ(Result::Error,
(new BytesConsumerTestUtil::TwoPhaseReader(dest2))->run().first);
EXPECT_EQ(BytesConsumer::PublicState::Errored, dest1->getPublicState());
EXPECT_EQ(numOnStateChangeCalled + 1, client->numOnStateChangeCalled());
EXPECT_EQ('a', buffer[0]);
EXPECT_EQ(Result::Ok, dest1->endRead(available));
}
TEST_F(BytesConsumerTeeTest,
AsyncNotificationShouldBeDispatchedWhenAllDataIsConsumed) {
ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
src->add(Command(Command::Data, "a"));
src->add(Command(Command::Wait));
src->add(Command(Command::Done));
TestClient* client = new TestClient();
BytesConsumer* dest1 = nullptr;
BytesConsumer* dest2 = nullptr;
BytesConsumer::tee(document(), src, &dest1, &dest2);
dest1->setClient(client);
const char* buffer = nullptr;
size_t available = 0;
ASSERT_EQ(Result::Ok, dest1->beginRead(&buffer, &available));
ASSERT_EQ(1u, available);
EXPECT_EQ('a', buffer[0]);
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
src->getPublicState());
testing::runPendingTasks();
EXPECT_EQ(BytesConsumer::PublicState::Closed, src->getPublicState());
// Just for checking UAF.
EXPECT_EQ('a', buffer[0]);
ASSERT_EQ(Result::Ok, dest1->endRead(1));
EXPECT_EQ(0, client->numOnStateChangeCalled());
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest1->getPublicState());
testing::runPendingTasks();
EXPECT_EQ(1, client->numOnStateChangeCalled());
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
}
TEST_F(BytesConsumerTeeTest,
AsyncCloseNotificationShouldBeCancelledBySubsequentReadCall) {
ReplayingBytesConsumer* src = new ReplayingBytesConsumer(document());
src->add(Command(Command::Data, "a"));
src->add(Command(Command::Done));
TestClient* client = new TestClient();
BytesConsumer* dest1 = nullptr;
BytesConsumer* dest2 = nullptr;
BytesConsumer::tee(document(), src, &dest1, &dest2);
dest1->setClient(client);
const char* buffer = nullptr;
size_t available = 0;
ASSERT_EQ(Result::Ok, dest1->beginRead(&buffer, &available));
ASSERT_EQ(1u, available);
EXPECT_EQ('a', buffer[0]);
testing::runPendingTasks();
// Just for checking UAF.
EXPECT_EQ('a', buffer[0]);
ASSERT_EQ(Result::Ok, dest1->endRead(1));
EXPECT_EQ(BytesConsumer::PublicState::ReadableOrWaiting,
dest1->getPublicState());
EXPECT_EQ(Result::Done, dest1->beginRead(&buffer, &available));
EXPECT_EQ(0, client->numOnStateChangeCalled());
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
testing::runPendingTasks();
EXPECT_EQ(0, client->numOnStateChangeCalled());
EXPECT_EQ(BytesConsumer::PublicState::Closed, dest1->getPublicState());
}
TEST(BytesConusmerTest, ClosedBytesConsumer) {
BytesConsumer* consumer = BytesConsumer::createClosed();
const char* buffer = nullptr;
size_t available = 0;
EXPECT_EQ(Result::Done, consumer->beginRead(&buffer, &available));
EXPECT_EQ(BytesConsumer::PublicState::Closed, consumer->getPublicState());
}
TEST(BytesConusmerTest, ErroredBytesConsumer) {
BytesConsumer::Error error("hello");
BytesConsumer* consumer = BytesConsumer::createErrored(error);
const char* buffer = nullptr;
size_t available = 0;
EXPECT_EQ(Result::Error, consumer->beginRead(&buffer, &available));
EXPECT_EQ(BytesConsumer::PublicState::Errored, consumer->getPublicState());
EXPECT_EQ(error.message(), consumer->getError().message());
consumer->cancel();
EXPECT_EQ(BytesConsumer::PublicState::Errored, consumer->getPublicState());
}
} // namespace
} // namespace blink