Commit 37d1ee1a authored by Rakina Zata Amni's avatar Rakina Zata Amni Committed by Commit Bot

Reland BufferedBody with queue + fix to write body ASAP + behind flag

The BufferedBody CL got reverted at https://crrev.com/c/2532869 because
it introduced flakes. This CL relands it and adds a potential fix:
write the drained body gradually, as previously we won't write to the
newly created pipe until we've finished draining completely.

This CL also changes the buffered_body_ data type to use queue of chunks
so that we can free memory as soon as we finished writing a chunk of
the response body to prevent potential OOMs, and protects all of this
code behind a flag (later on we'll also add a check so that we'll buffer
the body only when we're deferring due to bfcache)

Bug: 1147081, 1148130, 1137682
Change-Id: I61d95c9c67220a296daa66f7409f27fdeb0e8182
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2531338Reviewed-by: default avatarYutaka Hirano <yhirano@chromium.org>
Reviewed-by: default avatarAlexander Timin <altimin@chromium.org>
Commit-Queue: Rakina Zata Amni <rakina@chromium.org>
Cr-Commit-Position: refs/heads/master@{#827172}
parent 5e1e2457
......@@ -14,6 +14,7 @@
#include "content/public/common/url_utils.h"
#include "content/public/renderer/content_renderer_client.h"
#include "content/renderer/loader/resource_dispatcher.h"
#include "mojo/public/cpp/system/data_pipe_drainer.h"
#include "net/url_request/redirect_info.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
......@@ -143,6 +144,115 @@ class URLLoaderClientImpl::DeferredOnComplete final : public DeferredMessage {
const network::URLLoaderCompletionStatus status_;
};
class URLLoaderClientImpl::BodyBuffer final
: public mojo::DataPipeDrainer::Client {
public:
BodyBuffer(URLLoaderClientImpl* owner,
mojo::ScopedDataPipeConsumerHandle readable,
mojo::ScopedDataPipeProducerHandle writable,
scoped_refptr<base::SingleThreadTaskRunner> task_runner)
: owner_(owner),
writable_(std::move(writable)),
writable_watcher_(FROM_HERE,
mojo::SimpleWatcher::ArmingPolicy::MANUAL,
std::move(task_runner)) {
pipe_drainer_ =
std::make_unique<mojo::DataPipeDrainer>(this, std::move(readable));
writable_watcher_.Watch(
writable_.get(),
MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
base::BindRepeating(&BodyBuffer::WriteBufferedBody,
base::Unretained(this)));
}
bool active() const { return writable_watcher_.IsWatching(); }
// mojo::DataPipeDrainer::Client
void OnDataAvailable(const void* data, size_t num_bytes) override {
DCHECK(draining_);
SCOPED_CRASH_KEY_NUMBER(OnDataAvailable, buffered_body_size,
buffered_body_.size());
SCOPED_CRASH_KEY_NUMBER(OnDataAvailable, data_bytes, num_bytes);
SCOPED_CRASH_KEY_STRING256(
OnDataAvailable, last_loaded_url,
owner_->last_loaded_url().possibly_invalid_spec());
buffered_body_.emplace(static_cast<const char*>(data),
static_cast<const char*>(data) + num_bytes);
WriteBufferedBody(MOJO_RESULT_OK);
}
void OnDataComplete() override {
DCHECK(draining_);
draining_ = false;
WriteBufferedBody(MOJO_RESULT_OK);
}
private:
void WriteBufferedBody(MojoResult) {
// Try to write all the remaining chunks in |buffered_body_|.
while (!buffered_body_.empty()) {
// Write the chunk at the front of |buffered_body_|.
const std::vector<char>& current_chunk = buffered_body_.front();
DCHECK_LE(offset_in_current_chunk_, current_chunk.size());
uint32_t bytes_sent = base::saturated_cast<uint32_t>(
current_chunk.size() - offset_in_current_chunk_);
MojoResult result =
writable_->WriteData(current_chunk.data() + offset_in_current_chunk_,
&bytes_sent, MOJO_WRITE_DATA_FLAG_NONE);
switch (result) {
case MOJO_RESULT_OK:
break;
case MOJO_RESULT_FAILED_PRECONDITION:
// The pipe is closed unexpectedly, finish writing now.
draining_ = false;
Finish();
return;
case MOJO_RESULT_SHOULD_WAIT:
writable_watcher_.ArmOrNotify();
return;
default:
NOTREACHED();
return;
}
// We've sent |bytes_sent| bytes, update the current offset in the
// frontmost chunk.
offset_in_current_chunk_ += bytes_sent;
DCHECK_LE(offset_in_current_chunk_, current_chunk.size());
if (offset_in_current_chunk_ == current_chunk.size()) {
// We've finished writing the chunk at the front of the queue, pop it so
// that we'll write the next chunk next time.
buffered_body_.pop();
offset_in_current_chunk_ = 0;
}
}
// We're finished if we've drained the original pipe and sent all the
// buffered body.
if (!draining_)
Finish();
}
void Finish() {
DCHECK(!draining_);
// We've read and written all the data from the original pipe.
writable_watcher_.Cancel();
writable_.reset();
// There might be a deferred OnComplete message waiting for us to finish
// draining the response body, so flush the deferred messages in
// the owner URLLoaderClientImpl.
owner_->FlushDeferredMessages();
}
URLLoaderClientImpl* const owner_;
mojo::ScopedDataPipeProducerHandle writable_;
mojo::SimpleWatcher writable_watcher_;
std::unique_ptr<mojo::DataPipeDrainer> pipe_drainer_;
// We save the received response body as a queue of chunks so that we can free
// memory as soon as we finish sending a chunk completely.
base::queue<std::vector<char>> buffered_body_;
uint32_t offset_in_current_chunk_ = 0;
bool draining_ = true;
};
URLLoaderClientImpl::URLLoaderClientImpl(
int request_id,
ResourceDispatcher* resource_dispatcher,
......@@ -223,6 +333,15 @@ void URLLoaderClientImpl::FlushDeferredMessages() {
if (has_completion_message) {
DCHECK_GT(messages.size(), 0u);
DCHECK(messages.back()->IsCompletionMessage());
if (body_buffer_ && body_buffer_->active()) {
// If we still have an active body buffer, it means we haven't drained all
// of the contents of the response body yet. We shouldn't dispatch the
// completion message now, so
// put the message back into |deferred_messages_| to be sent later after
// the body buffer is no longer active.
deferred_messages_.emplace_back(std::move(messages.back()));
return;
}
messages.back()->HandleMessage(resource_dispatcher_, request_id_);
}
}
......@@ -321,13 +440,41 @@ void URLLoaderClientImpl::OnStartLoadingResponseBody(
base::TimeTicks::Now() - on_receive_response_time_);
}
if (NeedsStoringMessage()) {
StoreAndDispatch(
std::make_unique<DeferredOnStartLoadingResponseBody>(std::move(body)));
} else {
if (!NeedsStoringMessage()) {
// Send the message immediately.
resource_dispatcher_->OnStartLoadingResponseBody(request_id_,
std::move(body));
return;
}
if (!base::FeatureList::IsEnabled(
blink::features::kLoadingTasksUnfreezable)) {
// Defer the message, storing the original body pipe.
StoreAndDispatch(
std::make_unique<DeferredOnStartLoadingResponseBody>(std::move(body)));
return;
}
// We want to run loading tasks while deferred (but without dispatching the
// messages). Drain the original pipe containing the response body into a
// new pipe so that we won't block the network service if we're deferred for
// a long time.
mojo::ScopedDataPipeProducerHandle new_body_producer;
mojo::ScopedDataPipeConsumerHandle new_body_consumer;
MojoResult result =
mojo::CreateDataPipe(nullptr, &new_body_producer, &new_body_consumer);
if (result != MOJO_RESULT_OK) {
// We failed to make a new pipe, close the connections and dispatch an
// OnComplete message instead.
url_loader_.reset();
url_loader_client_receiver_.reset();
OnComplete(
network::URLLoaderCompletionStatus(net::ERR_INSUFFICIENT_RESOURCES));
return;
}
body_buffer_ = std::make_unique<BodyBuffer>(
this, std::move(body), std::move(new_body_producer), task_runner_);
StoreAndDispatch(std::make_unique<DeferredOnStartLoadingResponseBody>(
std::move(new_body_consumer)));
}
void URLLoaderClientImpl::OnComplete(
......
......@@ -79,6 +79,7 @@ class CONTENT_EXPORT URLLoaderClientImpl final
const GURL& last_loaded_url() const { return last_loaded_url_; }
private:
class BodyBuffer;
class DeferredMessage;
class DeferredOnReceiveResponse;
class DeferredOnReceiveRedirect;
......@@ -92,6 +93,7 @@ class CONTENT_EXPORT URLLoaderClientImpl final
void OnConnectionClosed();
std::vector<std::unique_ptr<DeferredMessage>> deferred_messages_;
std::unique_ptr<BodyBuffer> body_buffer_;
const int request_id_;
bool has_received_response_head_ = false;
bool has_received_response_body_ = false;
......
......@@ -6,6 +6,7 @@
#include <vector>
#include "base/run_loop.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "content/renderer/loader/resource_dispatcher.h"
#include "content/renderer/loader/test_request_peer.h"
......@@ -18,6 +19,7 @@
#include "services/network/public/mojom/url_loader_factory.mojom.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom.h"
#include "third_party/blink/public/platform/resource_load_info_notifier_wrapper.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
......@@ -48,9 +50,14 @@ std::string GetRequestPeerContextBody(TestRequestPeer::Context* context) {
} // namespace
class URLLoaderClientImplTest : public ::testing::Test,
public network::mojom::URLLoaderFactory {
public network::mojom::URLLoaderFactory,
public ::testing::WithParamInterface<bool> {
protected:
URLLoaderClientImplTest() : dispatcher_(new ResourceDispatcher()) {
if (BufferingEnabled()) {
scoped_feature_list_.InitAndEnableFeature(
blink::features::kLoadingTasksUnfreezable);
}
auto request = std::make_unique<network::ResourceRequest>();
request_id_ = dispatcher_->StartAsync(
std::move(request), 0 /* loader_option */,
......@@ -68,6 +75,8 @@ class URLLoaderClientImplTest : public ::testing::Test,
EXPECT_TRUE(url_loader_client_);
}
bool BufferingEnabled() { return GetParam(); }
void TearDown() override { url_loader_client_.reset(); }
void CreateLoaderAndStart(
......@@ -97,13 +106,14 @@ class URLLoaderClientImplTest : public ::testing::Test,
}
base::test::SingleThreadTaskEnvironment task_environment_;
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<ResourceDispatcher> dispatcher_;
TestRequestPeer::Context request_peer_context_;
int request_id_ = 0;
mojo::Remote<network::mojom::URLLoaderClient> url_loader_client_;
};
TEST_F(URLLoaderClientImplTest, OnReceiveResponse) {
TEST_P(URLLoaderClientImplTest, OnReceiveResponse) {
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
EXPECT_FALSE(request_peer_context_.received_response);
......@@ -111,7 +121,7 @@ TEST_F(URLLoaderClientImplTest, OnReceiveResponse) {
EXPECT_TRUE(request_peer_context_.received_response);
}
TEST_F(URLLoaderClientImplTest, ResponseBody) {
TEST_P(URLLoaderClientImplTest, ResponseBody) {
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
EXPECT_FALSE(request_peer_context_.received_response);
......@@ -131,7 +141,7 @@ TEST_F(URLLoaderClientImplTest, ResponseBody) {
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
}
TEST_F(URLLoaderClientImplTest, OnReceiveRedirect) {
TEST_P(URLLoaderClientImplTest, OnReceiveRedirect) {
net::RedirectInfo redirect_info;
url_loader_client_->OnReceiveRedirect(redirect_info,
......@@ -142,7 +152,7 @@ TEST_F(URLLoaderClientImplTest, OnReceiveRedirect) {
EXPECT_EQ(1, request_peer_context_.seen_redirects);
}
TEST_F(URLLoaderClientImplTest, OnReceiveCachedMetadata) {
TEST_P(URLLoaderClientImplTest, OnReceiveCachedMetadata) {
std::vector<uint8_t> data;
data.push_back('a');
mojo_base::BigBuffer metadata(data);
......@@ -158,7 +168,7 @@ TEST_F(URLLoaderClientImplTest, OnReceiveCachedMetadata) {
EXPECT_EQ('a', request_peer_context_.cached_metadata.data()[0]);
}
TEST_F(URLLoaderClientImplTest, OnTransferSizeUpdated) {
TEST_P(URLLoaderClientImplTest, OnTransferSizeUpdated) {
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
url_loader_client_->OnTransferSizeUpdated(4);
url_loader_client_->OnTransferSizeUpdated(4);
......@@ -170,7 +180,7 @@ TEST_F(URLLoaderClientImplTest, OnTransferSizeUpdated) {
EXPECT_EQ(8, request_peer_context_.total_encoded_data_length);
}
TEST_F(URLLoaderClientImplTest, OnCompleteWithResponseBody) {
TEST_P(URLLoaderClientImplTest, OnCompleteWithResponseBody) {
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
......@@ -203,7 +213,7 @@ TEST_F(URLLoaderClientImplTest, OnCompleteWithResponseBody) {
// Due to the lack of ordering guarantee, it is possible that the response body
// bytes arrives after the completion message. URLLoaderClientImpl should
// restore the order.
TEST_F(URLLoaderClientImplTest, OnCompleteShouldBeTheLastMessage) {
TEST_P(URLLoaderClientImplTest, OnCompleteShouldBeTheLastMessage) {
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
......@@ -226,7 +236,7 @@ TEST_F(URLLoaderClientImplTest, OnCompleteShouldBeTheLastMessage) {
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
}
TEST_F(URLLoaderClientImplTest, CancelOnReceiveResponse) {
TEST_P(URLLoaderClientImplTest, CancelOnReceiveResponse) {
request_peer_context_.cancel_on_receive_response = true;
network::URLLoaderCompletionStatus status;
......@@ -247,7 +257,7 @@ TEST_F(URLLoaderClientImplTest, CancelOnReceiveResponse) {
EXPECT_TRUE(request_peer_context_.cancelled);
}
TEST_F(URLLoaderClientImplTest, Defer) {
TEST_P(URLLoaderClientImplTest, Defer) {
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
......@@ -275,16 +285,16 @@ TEST_F(URLLoaderClientImplTest, Defer) {
EXPECT_TRUE(request_peer_context_.complete);
}
TEST_F(URLLoaderClientImplTest, DeferWithResponseBody) {
TEST_P(URLLoaderClientImplTest, DeferWithResponseBody) {
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
mojo::DataPipe data_pipe(DataPipeOptions());
uint32_t size = 5;
MojoResult result = data_pipe.producer_handle->WriteData(
"hello", &size, MOJO_WRITE_DATA_FLAG_NONE);
ASSERT_EQ(MOJO_RESULT_OK, result);
EXPECT_EQ(5u, size);
std::string msg1 = "hello";
uint32_t size = msg1.size();
ASSERT_EQ(MOJO_RESULT_OK, data_pipe.producer_handle->WriteData(
msg1.data(), &size, MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(msg1.size(), size);
data_pipe.producer_handle.reset();
url_loader_client_->OnStartLoadingResponseBody(
......@@ -313,9 +323,175 @@ TEST_F(URLLoaderClientImplTest, DeferWithResponseBody) {
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
}
TEST_P(URLLoaderClientImplTest, StoppedDeferringBeforeClosing) {
// Call OnReceiveResponse, OnStartLoadingResponseBody, OnComplete while
// deferred.
dispatcher_->SetDefersLoading(request_id_, true);
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(MOJO_RESULT_OK,
mojo::CreateDataPipe(nullptr, &producer_handle, &consumer_handle));
url_loader_client_->OnStartLoadingResponseBody(std::move(consumer_handle));
network::URLLoaderCompletionStatus status;
url_loader_client_->OnComplete(status);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
// Write data to the response body pipe, but don't close the connection yet.
std::string msg1 = "hello";
uint32_t size = msg1.size();
// We expect that the other end of the pipe to be ready to read the data
// immediately.
ASSERT_EQ(MOJO_RESULT_OK, producer_handle->WriteData(
msg1.data(), &size, MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(msg1.size(), size);
base::RunLoop().RunUntilIdle();
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
// Stop deferring. OnComplete message shouldn't be dispatched yet because
// we're still waiting for the response body pipe to be closed.
dispatcher_->SetDefersLoading(request_id_, false);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
// When the body is buffered, we'll wait until the pipe is closed before
// sending the OnComplete message.
EXPECT_NE(BufferingEnabled(), request_peer_context_.complete);
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
// Write more data to the pipe while not deferred.
std::string msg2 = "world";
size = msg2.size();
ASSERT_EQ(MOJO_RESULT_OK, producer_handle->WriteData(
msg2.data(), &size, MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(msg2.size(), size);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_NE(BufferingEnabled(), request_peer_context_.complete);
EXPECT_EQ("helloworld", GetRequestPeerContextBody(&request_peer_context_));
// Close the response body pipe.
producer_handle.reset();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_TRUE(request_peer_context_.complete);
EXPECT_EQ("helloworld", GetRequestPeerContextBody(&request_peer_context_));
}
TEST_P(URLLoaderClientImplTest, DeferBodyWithoutOnComplete) {
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
// Call OnStartLoadingResponseBody while deferred.
dispatcher_->SetDefersLoading(request_id_, true);
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(MOJO_RESULT_OK,
mojo::CreateDataPipe(nullptr, &producer_handle, &consumer_handle));
url_loader_client_->OnStartLoadingResponseBody(std::move(consumer_handle));
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
// Write data to the response body pipe, but don't close the connection yet.
std::string msg1 = "hello";
uint32_t size = msg1.size();
// We expect that the other end of the pipe to be ready to read the data
// immediately.
ASSERT_EQ(MOJO_RESULT_OK, producer_handle->WriteData(
msg1.data(), &size, MOJO_WRITE_DATA_FLAG_NONE));
EXPECT_EQ(msg1.size(), size);
base::RunLoop().RunUntilIdle();
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
// Stop deferring.
dispatcher_->SetDefersLoading(request_id_, false);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
// Close the response body pipe.
producer_handle.reset();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("hello", GetRequestPeerContextBody(&request_peer_context_));
}
TEST_P(URLLoaderClientImplTest, DeferredWithLongResponseBody) {
// Call OnReceiveResponse, OnStartLoadingResponseBody, OnComplete while
// deferred.
dispatcher_->SetDefersLoading(request_id_, true);
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
mojo::ScopedDataPipeProducerHandle producer_handle;
mojo::ScopedDataPipeConsumerHandle consumer_handle;
ASSERT_EQ(MOJO_RESULT_OK,
mojo::CreateDataPipe(nullptr, &producer_handle, &consumer_handle));
url_loader_client_->OnStartLoadingResponseBody(std::move(consumer_handle));
network::URLLoaderCompletionStatus status;
url_loader_client_->OnComplete(status);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(request_peer_context_.received_response);
EXPECT_FALSE(request_peer_context_.complete);
EXPECT_EQ("", GetRequestPeerContextBody(&request_peer_context_));
// Write to the response body pipe. It will take several writes.
const uint32_t body_size = 70000;
uint32_t bytes_remaining = body_size;
std::string body(body_size, '*');
while (bytes_remaining > 0) {
uint32_t start_position = body_size - bytes_remaining;
uint32_t bytes_sent = bytes_remaining;
MojoResult result = producer_handle->WriteData(
body.c_str() + start_position, &bytes_sent, MOJO_WRITE_DATA_FLAG_NONE);
if (result == MOJO_RESULT_SHOULD_WAIT) {
if (BufferingEnabled()) {
// When we buffer the body the pipe gets drained asynchronously, so it's
// possible to keep writing to the pipe if we wait.
base::RunLoop().RunUntilIdle();
continue;
} else
// When we don't buffer the body, however, nothing is draining the pipe
// so we can't write more even if we wait.
break;
}
EXPECT_EQ(MOJO_RESULT_OK, result);
EXPECT_GE(bytes_remaining, bytes_sent);
bytes_remaining -= bytes_sent;
}
// Ensure we've written all that we can write. When buffering is disabled, we
// can only write |body_size| - |bytes_remaining| bytes.
const uint32_t bytes_written = body_size - bytes_remaining;
DCHECK_EQ(BufferingEnabled(), body_size == bytes_written);
producer_handle.reset();
// Stop deferring.
dispatcher_->SetDefersLoading(request_id_, false);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_peer_context_.received_response);
// When the body is buffered, BodyBuffer shouldn't be finished writing to the
// new response body pipe at this point (because nobody is reading it).
EXPECT_NE(BufferingEnabled(), request_peer_context_.complete);
// Calling GetRequestPeerContextBody to read data from the new response body
// pipe will make BodyBuffer write the rest of the body to the pipe.
uint32_t bytes_read = 0;
while (bytes_read < bytes_written) {
bytes_read = GetRequestPeerContextBody(&request_peer_context_).size();
base::RunLoop().RunUntilIdle();
}
// Ensure that we've read everything we've written.
EXPECT_EQ(bytes_written, bytes_read);
EXPECT_EQ(body.substr(0, bytes_written),
GetRequestPeerContextBody(&request_peer_context_));
EXPECT_TRUE(request_peer_context_.complete);
}
// As "transfer size update" message is handled specially in the implementation,
// we have a separate test.
TEST_F(URLLoaderClientImplTest, DeferWithTransferSizeUpdated) {
TEST_P(URLLoaderClientImplTest, DeferWithTransferSizeUpdated) {
network::URLLoaderCompletionStatus status;
url_loader_client_->OnReceiveResponse(network::mojom::URLResponseHead::New());
......@@ -358,7 +534,7 @@ TEST_F(URLLoaderClientImplTest, DeferWithTransferSizeUpdated) {
EXPECT_EQ(4, request_peer_context_.total_encoded_data_length);
}
TEST_F(URLLoaderClientImplTest, SetDeferredDuringFlushingDeferredMessage) {
TEST_P(URLLoaderClientImplTest, SetDeferredDuringFlushingDeferredMessage) {
request_peer_context_.defer_on_redirect = true;
net::RedirectInfo redirect_info;
......@@ -420,7 +596,7 @@ TEST_F(URLLoaderClientImplTest, SetDeferredDuringFlushingDeferredMessage) {
EXPECT_FALSE(request_peer_context_.cancelled);
}
TEST_F(URLLoaderClientImplTest,
TEST_P(URLLoaderClientImplTest,
SetDeferredDuringFlushingDeferredMessageOnTransferSizeUpdated) {
request_peer_context_.defer_on_transfer_size_updated = true;
......@@ -465,4 +641,6 @@ TEST_F(URLLoaderClientImplTest,
EXPECT_FALSE(request_peer_context_.cancelled);
}
INSTANTIATE_TEST_SUITE_P(All, URLLoaderClientImplTest, ::testing::Bool());
} // namespace content
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment