| Index: content/browser/loader/buffered_resource_handler.cc
|
| diff --git a/content/browser/loader/buffered_resource_handler.cc b/content/browser/loader/buffered_resource_handler.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..381d7156387899335be139959ea4d945c8f2878e
|
| --- /dev/null
|
| +++ b/content/browser/loader/buffered_resource_handler.cc
|
| @@ -0,0 +1,266 @@
|
| +// 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 "content/browser/loader/buffered_resource_handler.h"
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/location.h"
|
| +#include "base/logging.h"
|
| +#include "content/public/common/resource_response.h"
|
| +#include "net/base/io_buffer.h"
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +// Used to write into an existing IOBuffer at a given offset.
|
| +class DependentIOBuffer : public net::WrappedIOBuffer {
|
| + public:
|
| + DependentIOBuffer(net::IOBuffer* buf, int offset)
|
| + : net::WrappedIOBuffer(buf->data() + offset), buf_(buf) {}
|
| +
|
| + private:
|
| + ~DependentIOBuffer() override {}
|
| +
|
| + scoped_refptr<net::IOBuffer> buf_;
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +BufferedResourceHandler::BufferedResourceHandler(
|
| + std::unique_ptr<ResourceHandler> next_handler,
|
| + net::URLRequest* request,
|
| + int max_bytes_to_buffer)
|
| + : LayeredResourceHandler(request, std::move(next_handler)),
|
| + state_(STATE_STARTING),
|
| + max_bytes_to_buffer_(max_bytes_to_buffer),
|
| + read_buffer_size_(0),
|
| + bytes_read_(0),
|
| + weak_ptr_factory_(this) {}
|
| +
|
| +BufferedResourceHandler::~BufferedResourceHandler() {}
|
| +
|
| +bool BufferedResourceHandler::CanStartReplayInResponseStarted() {
|
| + return true;
|
| +}
|
| +
|
| +bool BufferedResourceHandler::CanStartReplayInReadCompleted() {
|
| + return true;
|
| +}
|
| +
|
| +bool BufferedResourceHandler::WillStartReplay(bool* defer) {
|
| + return true;
|
| +}
|
| +
|
| +void BufferedResourceHandler::SetController(ResourceController* controller) {
|
| + ResourceHandler::SetController(controller);
|
| +
|
| + // Downstream handlers see the BufferedResourceHandler as their
|
| + // ResourceController, which allows it to consume part or all of the resource
|
| + // response, and then later replay it to downstream handler.
|
| + DCHECK(next_handler_.get());
|
| + next_handler_->SetController(this);
|
| +}
|
| +
|
| +bool BufferedResourceHandler::OnResponseStarted(ResourceResponse* response,
|
| + bool* defer) {
|
| + response_ = response;
|
| +
|
| + state_ = STATE_BUFFERING;
|
| +
|
| + if (!CanStartReplayInResponseStarted()) {
|
| + return true;
|
| + }
|
| +
|
| + return ProcessReplay(defer);
|
| +}
|
| +
|
| +bool BufferedResourceHandler::OnWillRead(scoped_refptr<net::IOBuffer>* buf,
|
| + int* buf_size,
|
| + int min_size) {
|
| + if (state_ == STATE_STREAMING)
|
| + return next_handler_->OnWillRead(buf, buf_size, min_size);
|
| +
|
| + DCHECK_EQ(-1, min_size);
|
| +
|
| + if (read_buffer_.get()) {
|
| + CHECK_LT(bytes_read_, read_buffer_size_);
|
| + *buf = new DependentIOBuffer(read_buffer_.get(), bytes_read_);
|
| + *buf_size = read_buffer_size_ - bytes_read_;
|
| + } else {
|
| + if (!next_handler_->OnWillRead(buf, buf_size, min_size))
|
| + return false;
|
| +
|
| + read_buffer_ = *buf;
|
| + read_buffer_size_ = *buf_size;
|
| + DCHECK_GE(read_buffer_size_, max_bytes_to_buffer_ * 2);
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +bool BufferedResourceHandler::OnReadCompleted(int bytes_read, bool* defer) {
|
| + if (state_ == STATE_STREAMING)
|
| + return next_handler_->OnReadCompleted(bytes_read, defer);
|
| +
|
| + DCHECK_EQ(state_, STATE_BUFFERING);
|
| + bytes_read_ += bytes_read;
|
| +
|
| + if (!CanStartReplayInReadCompleted() && (bytes_read > 0))
|
| + return true;
|
| +
|
| + return ProcessReplay(defer);
|
| +}
|
| +
|
| +void BufferedResourceHandler::OnResponseCompleted(
|
| + const net::URLRequestStatus& status,
|
| + const std::string& security_info,
|
| + bool* defer) {
|
| + // Upon completion, act like a pass-through handler in case the downstream
|
| + // handler defers OnResponseCompleted.
|
| + state_ = STATE_STREAMING;
|
| +
|
| + next_handler_->OnResponseCompleted(status, security_info, defer);
|
| +}
|
| +
|
| +void BufferedResourceHandler::InstallNewLeafHandler(
|
| + std::unique_ptr<ResourceHandler> new_handler,
|
| + const std::string& payload_for_old_handler) {
|
| + // If acting as a pass-through handler, install the new leaf handler right
|
| + // away.
|
| + if (state_ == STATE_STARTING || state_ == STATE_STREAMING) {
|
| + LayeredResourceHandler::InstallNewLeafHandler(std::move(new_handler),
|
| + payload_for_old_handler);
|
| + return;
|
| + }
|
| +
|
| + // Store the handler so that it can be properly installed when replaying the
|
| + // response.
|
| + new_leaf_handler_ = std::move(new_handler);
|
| + payload_for_old_handler_ = payload_for_old_handler;
|
| + new_leaf_handler_->SetController(this);
|
| +}
|
| +
|
| +void BufferedResourceHandler::Resume() {
|
| + // If no information is currently being transmitted to downstream handlers,
|
| + // they should not attempt to resume the request.
|
| + if (state_ == STATE_BUFFERING || state_ == STATE_PROCESSING) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +
|
| + // If the BufferingHandler is acting as a pass-through handler, just ask the
|
| + // upwards ResourceController to resume the request.
|
| + if (state_ == STATE_STARTING || state_ == STATE_STREAMING) {
|
| + controller()->Resume();
|
| + return;
|
| + }
|
| +
|
| + // Otherwise proceed with the replay of the response. If it is successful,
|
| + // resume the request.
|
| + bool defer = false;
|
| + if (!ProcessReplay(&defer)) {
|
| + Cancel();
|
| + } else if (!defer) {
|
| + DCHECK(state_ == STATE_STREAMING);
|
| + controller()->Resume();
|
| + }
|
| +}
|
| +
|
| +void BufferedResourceHandler::Cancel() {
|
| + controller()->Cancel();
|
| +}
|
| +
|
| +void BufferedResourceHandler::CancelAndIgnore() {
|
| + controller()->CancelAndIgnore();
|
| +}
|
| +
|
| +void BufferedResourceHandler::CancelWithError(int error_code) {
|
| + controller()->CancelWithError(error_code);
|
| +}
|
| +
|
| +bool BufferedResourceHandler::ProcessReplay(bool* defer) {
|
| + bool return_value = true;
|
| + while (!*defer && return_value && state_ != STATE_STREAMING) {
|
| + switch (state_) {
|
| + case STATE_BUFFERING:
|
| + return_value = MaybeStartReplay(defer);
|
| + break;
|
| + case STATE_PROCESSING:
|
| + return_value = ReplayResponseReceived(defer);
|
| + break;
|
| + case STATE_REPLAYING_RESPONSE_RECEIVED:
|
| + return_value = ReplayResponseReceivedNewHandler(defer);
|
| + break;
|
| + case STATE_REPLAYING_RESPONSE_RECEIVED_NEW_HANDLER:
|
| + return_value = ReplayReadCompleted(defer);
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + break;
|
| + }
|
| + }
|
| + return return_value;
|
| +}
|
| +
|
| +bool BufferedResourceHandler::MaybeStartReplay(bool* defer) {
|
| + if (!WillStartReplay(defer))
|
| + return false;
|
| +
|
| + if (*defer)
|
| + state_ = STATE_BUFFERING;
|
| + else
|
| + state_ = STATE_PROCESSING;
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool BufferedResourceHandler::ReplayResponseReceived(bool* defer) {
|
| + DCHECK(state_ == STATE_PROCESSING);
|
| + state_ = STATE_REPLAYING_RESPONSE_RECEIVED;
|
| + return next_handler_->OnResponseStarted(response_.get(), defer);
|
| +}
|
| +
|
| +bool BufferedResourceHandler::ReplayResponseReceivedNewHandler(bool* defer) {
|
| + DCHECK(state_ == STATE_REPLAYING_RESPONSE_RECEIVED);
|
| + state_ = STATE_REPLAYING_RESPONSE_RECEIVED_NEW_HANDLER;
|
| + if (new_leaf_handler_)
|
| + return new_leaf_handler_->OnResponseStarted(response_.get(), defer);
|
| + return true;
|
| +}
|
| +
|
| +bool BufferedResourceHandler::ReplayReadCompleted(bool* defer) {
|
| + DCHECK(state_ == STATE_REPLAYING_RESPONSE_RECEIVED_NEW_HANDLER);
|
| +
|
| + // First, install the new leaf ResourceHandler (if any), before calling
|
| + // OnReadCompleted on the downstream ResourceHandlers. This should also take
|
| + // care of delivering the payload for the old ResourceHandler.
|
| + if (new_leaf_handler_) {
|
| + // Copy the buffered data to the new leaf ResourceHandler if needed.
|
| + if (read_buffer_.get()) {
|
| + scoped_refptr<net::IOBuffer> new_buffer;
|
| + int buf_len = 0;
|
| + if (!new_leaf_handler_->OnWillRead(&new_buffer, &buf_len, bytes_read_))
|
| + return false;
|
| + CHECK((buf_len >= bytes_read_) && (bytes_read_ >= 0));
|
| + memcpy(new_buffer->data(), read_buffer_->data(), bytes_read_);
|
| + }
|
| + LayeredResourceHandler::InstallNewLeafHandler(std::move(new_leaf_handler_),
|
| + payload_for_old_handler_);
|
| + }
|
| +
|
| + state_ = STATE_STREAMING;
|
| +
|
| + if (!read_buffer_.get())
|
| + return true;
|
| +
|
| + bool result = next_handler_->OnReadCompleted(bytes_read_, defer);
|
| +
|
| + read_buffer_ = NULL;
|
| + read_buffer_size_ = 0;
|
| + bytes_read_ = 0;
|
| +
|
| + return result;
|
| +}
|
| +
|
| +} // namespace content
|
|
|