Index: content/browser/loader/mojo_async_resource_handler.cc |
diff --git a/content/browser/loader/mojo_async_resource_handler.cc b/content/browser/loader/mojo_async_resource_handler.cc |
index 1c4fc78a631a9dd02eba9ae36b86a1679e778aad..571c71805ed2760d1f644e1d5db86861c5ff73d6 100644 |
--- a/content/browser/loader/mojo_async_resource_handler.cc |
+++ b/content/browser/loader/mojo_async_resource_handler.cc |
@@ -28,8 +28,8 @@ |
#include "content/public/common/resource_response.h" |
#include "mojo/public/c/system/data_pipe.h" |
#include "mojo/public/cpp/bindings/message.h" |
-#include "net/base/io_buffer.h" |
#include "net/base/mime_sniffer.h" |
+#include "net/base/net_errors.h" |
#include "net/url_request/redirect_info.h" |
namespace content { |
@@ -227,12 +227,20 @@ void MojoAsyncResourceHandler::OnWillRead( |
scoped_refptr<net::IOBuffer>* buf, |
int* buf_size, |
std::unique_ptr<ResourceController> controller) { |
+ // |buffer_| is set to nullptr on successful read completion (Except for the |
+ // final 0-byte read, so this DCHECK will also catch OnWillRead being called |
+ // after OnReadCompelted(0)). |
+ DCHECK(!buffer_); |
+ DCHECK_EQ(0u, buffer_offset_); |
+ |
if (!CheckForSufficientResource()) { |
controller->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); |
return; |
} |
+ bool first_call = false; |
if (!shared_writer_) { |
+ first_call = true; |
MojoCreateDataPipeOptions options; |
options.struct_size = sizeof(MojoCreateDataPipeOptions); |
options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; |
@@ -249,33 +257,38 @@ void MojoAsyncResourceHandler::OnWillRead( |
base::Bind(&MojoAsyncResourceHandler::OnWritable, |
base::Unretained(this))); |
handle_watcher_.ArmOrNotify(); |
+ } |
- bool defer = false; |
- scoped_refptr<net::IOBufferWithSize> buffer; |
- if (!AllocateWriterIOBuffer(&buffer, &defer)) { |
+ bool defer = false; |
+ if (!AllocateWriterIOBuffer(&buffer_, &defer)) { |
+ controller->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); |
+ return; |
+ } |
+ |
+ if (defer) { |
+ DCHECK(!buffer_); |
+ parent_buffer_ = buf; |
+ parent_buffer_size_ = buf_size; |
+ HoldController(std::move(controller)); |
+ request()->LogBlockedBy("MojoAsyncResourceHandler"); |
+ did_defer_on_will_read_ = true; |
+ return; |
+ } |
+ |
+ // The first call to OnWillRead must return a buffer of at least |
+ // kMinAllocationSize. If the Mojo buffer is too small, need to allocate an |
+ // intermediary buffer. |
+ if (first_call && static_cast<size_t>(buffer_->size()) < kMinAllocationSize) { |
+ // The allocated buffer is too small, so need to create an intermediary one. |
+ if (EndWrite(0) != MOJO_RESULT_OK) { |
controller->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); |
return; |
} |
- if (!defer) { |
- if (static_cast<size_t>(buffer->size()) >= kMinAllocationSize) { |
- *buf = buffer_ = buffer; |
- *buf_size = buffer_->size(); |
- controller->Resume(); |
- return; |
- } |
- |
- // The allocated buffer is too small. |
- if (EndWrite(0) != MOJO_RESULT_OK) { |
- controller->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); |
- return; |
- } |
- } |
DCHECK(!is_using_io_buffer_not_from_writer_); |
is_using_io_buffer_not_from_writer_ = true; |
buffer_ = new net::IOBufferWithSize(kMinAllocationSize); |
} |
- DCHECK_EQ(0u, buffer_offset_); |
*buf = buffer_; |
*buf_size = buffer_->size(); |
controller->Resume(); |
@@ -288,7 +301,9 @@ void MojoAsyncResourceHandler::OnReadCompleted( |
DCHECK_GE(bytes_read, 0); |
DCHECK(buffer_); |
- if (!bytes_read) { |
+ if (bytes_read == 0) { |
+ // Note that |buffer_| is not cleared here, which will cause a DCHECK on |
+ // subsequent OnWillRead calls. |
controller->Resume(); |
return; |
} |
@@ -308,12 +323,12 @@ void MojoAsyncResourceHandler::OnReadCompleted( |
} |
if (is_using_io_buffer_not_from_writer_) { |
- // Couldn't allocate a buffer on the data pipe in OnWillRead. |
+ // Couldn't allocate a large enough buffer on the data pipe in OnWillRead. |
DCHECK_EQ(0u, buffer_bytes_read_); |
buffer_bytes_read_ = bytes_read; |
bool defer = false; |
if (!CopyReadDataToDataPipe(&defer)) { |
- controller->Cancel(); |
+ controller->CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); |
return; |
} |
if (defer) { |
@@ -330,20 +345,8 @@ void MojoAsyncResourceHandler::OnReadCompleted( |
controller->Cancel(); |
return; |
} |
- // Allocate a buffer for the next OnWillRead call here, because OnWillRead |
- // doesn't have |defer| parameter. |
- bool defer = false; |
- if (!AllocateWriterIOBuffer(&buffer_, &defer)) { |
- controller->Cancel(); |
- return; |
- } |
- if (defer) { |
- request()->LogBlockedBy("MojoAsyncResourceHandler"); |
- did_defer_on_writing_ = true; |
- HoldController(std::move(controller)); |
- return; |
- } |
+ buffer_ = nullptr; |
controller->Resume(); |
} |
@@ -363,6 +366,7 @@ void MojoAsyncResourceHandler::FollowRedirect() { |
return; |
} |
+ DCHECK(!did_defer_on_will_read_); |
DCHECK(!did_defer_on_writing_); |
did_defer_on_redirect_ = false; |
request()->LogUnblocked(); |
@@ -454,17 +458,12 @@ void MojoAsyncResourceHandler::OnResponseCompleted( |
} |
bool MojoAsyncResourceHandler::CopyReadDataToDataPipe(bool* defer) { |
- while (true) { |
+ while (buffer_bytes_read_ > 0) { |
scoped_refptr<net::IOBufferWithSize> dest; |
if (!AllocateWriterIOBuffer(&dest, defer)) |
return false; |
if (*defer) |
return true; |
- if (buffer_bytes_read_ == 0) { |
- // All bytes are copied. Save the buffer for the next OnWillRead call. |
- buffer_ = std::move(dest); |
- return true; |
- } |
size_t copied_size = |
std::min(buffer_bytes_read_, static_cast<size_t>(dest->size())); |
@@ -473,13 +472,13 @@ bool MojoAsyncResourceHandler::CopyReadDataToDataPipe(bool* defer) { |
buffer_bytes_read_ -= copied_size; |
if (EndWrite(copied_size) != MOJO_RESULT_OK) |
return false; |
- |
- if (buffer_bytes_read_ == 0) { |
- // All bytes are copied. |
- buffer_offset_ = 0; |
- is_using_io_buffer_not_from_writer_ = false; |
- } |
} |
+ |
+ // All bytes are copied. |
+ buffer_ = nullptr; |
+ buffer_offset_ = 0; |
+ is_using_io_buffer_not_from_writer_ = false; |
+ return true; |
} |
bool MojoAsyncResourceHandler::AllocateWriterIOBuffer( |
@@ -494,6 +493,7 @@ bool MojoAsyncResourceHandler::AllocateWriterIOBuffer( |
} |
if (result != MOJO_RESULT_OK) |
return false; |
+ DCHECK_GT(available, 0u); |
*buf = new WriterIOBuffer(shared_writer_, data, available); |
return true; |
} |
@@ -510,26 +510,34 @@ bool MojoAsyncResourceHandler::CheckForSufficientResource() { |
} |
void MojoAsyncResourceHandler::OnWritable(MojoResult result) { |
+ if (did_defer_on_will_read_) { |
+ DCHECK(has_controller()); |
+ DCHECK(!did_defer_on_writing_); |
+ DCHECK(!did_defer_on_redirect_); |
+ |
+ did_defer_on_will_read_ = false; |
+ |
+ scoped_refptr<net::IOBuffer>* parent_buffer = parent_buffer_; |
+ parent_buffer_ = nullptr; |
+ int* parent_buffer_size = parent_buffer_size_; |
+ parent_buffer_size_ = nullptr; |
+ OnWillRead(parent_buffer, parent_buffer_size, ReleaseController()); |
+ return; |
+ } |
+ |
if (!did_defer_on_writing_) |
return; |
DCHECK(has_controller()); |
DCHECK(!did_defer_on_redirect_); |
did_defer_on_writing_ = false; |
- if (is_using_io_buffer_not_from_writer_) { |
- // |buffer_| is set to a net::IOBufferWithSize. Write the buffer contents |
- // to the data pipe. |
- DCHECK_GT(buffer_bytes_read_, 0u); |
- if (!CopyReadDataToDataPipe(&did_defer_on_writing_)) { |
- CancelWithError(net::ERR_FAILED); |
- return; |
- } |
- } else { |
- // Allocate a buffer for the next OnWillRead call here. |
- if (!AllocateWriterIOBuffer(&buffer_, &did_defer_on_writing_)) { |
- CancelWithError(net::ERR_FAILED); |
- return; |
- } |
+ DCHECK(is_using_io_buffer_not_from_writer_); |
+ // |buffer_| is set to a net::IOBufferWithSize. Write the buffer contents |
+ // to the data pipe. |
+ DCHECK_GT(buffer_bytes_read_, 0u); |
+ if (!CopyReadDataToDataPipe(&did_defer_on_writing_)) { |
+ CancelWithError(net::ERR_INSUFFICIENT_RESOURCES); |
+ return; |
} |
if (did_defer_on_writing_) { |