| Index: content/network/url_loader_impl.cc
|
| diff --git a/content/network/url_loader_impl.cc b/content/network/url_loader_impl.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1f6bc7d35aa89c612a27f2b7aa160642faeca2ae
|
| --- /dev/null
|
| +++ b/content/network/url_loader_impl.cc
|
| @@ -0,0 +1,391 @@
|
| +// Copyright 2017 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/network/url_loader_impl.h"
|
| +
|
| +#include "base/task_scheduler/post_task.h"
|
| +#include "base/threading/thread_task_runner_handle.h"
|
| +#include "content/network/net_adapters.h"
|
| +#include "content/network/network_context.h"
|
| +#include "content/public/common/referrer.h"
|
| +#include "content/public/common/resource_response.h"
|
| +#include "net/base/elements_upload_data_stream.h"
|
| +#include "net/base/load_flags.h"
|
| +#include "net/base/upload_bytes_element_reader.h"
|
| +#include "net/base/upload_file_element_reader.h"
|
| +#include "net/url_request/url_request_context.h"
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +constexpr size_t kDefaultAllocationSize = 512 * 1024;
|
| +
|
| +// TODO: this duplicates ResourceDispatcherHostImpl::BuildLoadFlagsForRequest.
|
| +int BuildLoadFlagsForRequest(const ResourceRequest& request,
|
| + bool is_sync_load) {
|
| + int load_flags = request.load_flags;
|
| +
|
| + // Although EV status is irrelevant to sub-frames and sub-resources, we have
|
| + // to perform EV certificate verification on all resources because an HTTP
|
| + // keep-alive connection created to load a sub-frame or a sub-resource could
|
| + // be reused to load a main frame.
|
| + load_flags |= net::LOAD_VERIFY_EV_CERT;
|
| + if (request.resource_type == RESOURCE_TYPE_MAIN_FRAME) {
|
| + load_flags |= net::LOAD_MAIN_FRAME_DEPRECATED;
|
| + } else if (request.resource_type == RESOURCE_TYPE_PREFETCH) {
|
| + load_flags |= net::LOAD_PREFETCH;
|
| + }
|
| +
|
| + if (is_sync_load)
|
| + load_flags |= net::LOAD_IGNORE_LIMITS;
|
| +
|
| + return load_flags;
|
| +}
|
| +
|
| +// TODO: this duplicates some of PopulateResourceResponse in
|
| +// content/browser/loader/resource_loader.cc
|
| +void PopulateResourceResponse(net::URLRequest* request,
|
| + ResourceResponse* response) {
|
| + response->head.request_time = request->request_time();
|
| + response->head.response_time = request->response_time();
|
| + response->head.headers = request->response_headers();
|
| + request->GetCharset(&response->head.charset);
|
| + response->head.content_length = request->GetExpectedContentSize();
|
| + request->GetMimeType(&response->head.mime_type);
|
| + net::HttpResponseInfo response_info = request->response_info();
|
| + response->head.was_fetched_via_spdy = response_info.was_fetched_via_spdy;
|
| + response->head.was_alpn_negotiated = response_info.was_alpn_negotiated;
|
| + response->head.alpn_negotiated_protocol =
|
| + response_info.alpn_negotiated_protocol;
|
| + response->head.connection_info = response_info.connection_info;
|
| + response->head.socket_address = response_info.socket_address;
|
| +
|
| + response->head.effective_connection_type =
|
| + net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN;
|
| +}
|
| +
|
| +// A subclass of net::UploadBytesElementReader which owns
|
| +// ResourceRequestBodyImpl.
|
| +class BytesElementReader : public net::UploadBytesElementReader {
|
| + public:
|
| + BytesElementReader(ResourceRequestBodyImpl* resource_request_body,
|
| + const ResourceRequestBodyImpl::Element& element)
|
| + : net::UploadBytesElementReader(element.bytes(), element.length()),
|
| + resource_request_body_(resource_request_body) {
|
| + DCHECK_EQ(ResourceRequestBodyImpl::Element::TYPE_BYTES, element.type());
|
| + }
|
| +
|
| + ~BytesElementReader() override {}
|
| +
|
| + private:
|
| + scoped_refptr<ResourceRequestBodyImpl> resource_request_body_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(BytesElementReader);
|
| +};
|
| +
|
| +// A subclass of net::UploadFileElementReader which owns
|
| +// ResourceRequestBodyImpl.
|
| +// This class is necessary to ensure the BlobData and any attached shareable
|
| +// files survive until upload completion.
|
| +class FileElementReader : public net::UploadFileElementReader {
|
| + public:
|
| + FileElementReader(ResourceRequestBodyImpl* resource_request_body,
|
| + base::TaskRunner* task_runner,
|
| + const ResourceRequestBodyImpl::Element& element)
|
| + : net::UploadFileElementReader(task_runner,
|
| + element.path(),
|
| + element.offset(),
|
| + element.length(),
|
| + element.expected_modification_time()),
|
| + resource_request_body_(resource_request_body) {
|
| + DCHECK_EQ(ResourceRequestBodyImpl::Element::TYPE_FILE, element.type());
|
| + }
|
| +
|
| + ~FileElementReader() override {}
|
| +
|
| + private:
|
| + scoped_refptr<ResourceRequestBodyImpl> resource_request_body_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(FileElementReader);
|
| +};
|
| +
|
| +// TODO: copied from content/browser/loader/upload_data_stream_builder.cc.
|
| +std::unique_ptr<net::UploadDataStream> CreateUploadDataStream(
|
| + ResourceRequestBodyImpl* body,
|
| + base::SequencedTaskRunner* file_task_runner) {
|
| + std::vector<std::unique_ptr<net::UploadElementReader>> element_readers;
|
| + for (const auto& element : *body->elements()) {
|
| + switch (element.type()) {
|
| + case ResourceRequestBodyImpl::Element::TYPE_BYTES:
|
| + element_readers.push_back(
|
| + base::MakeUnique<BytesElementReader>(body, element));
|
| + break;
|
| + case ResourceRequestBodyImpl::Element::TYPE_FILE:
|
| + element_readers.push_back(base::MakeUnique<FileElementReader>(
|
| + body, file_task_runner, element));
|
| + break;
|
| + case ResourceRequestBodyImpl::Element::TYPE_FILE_FILESYSTEM:
|
| + NOTIMPLEMENTED();
|
| + break;
|
| + case ResourceRequestBodyImpl::Element::TYPE_BLOB: {
|
| + NOTIMPLEMENTED();
|
| + break;
|
| + }
|
| + case ResourceRequestBodyImpl::Element::TYPE_DISK_CACHE_ENTRY:
|
| + case ResourceRequestBodyImpl::Element::TYPE_BYTES_DESCRIPTION:
|
| + case ResourceRequestBodyImpl::Element::TYPE_UNKNOWN:
|
| + NOTREACHED();
|
| + break;
|
| + }
|
| + }
|
| +
|
| + return base::MakeUnique<net::ElementsUploadDataStream>(
|
| + std::move(element_readers), body->identifier());
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +URLLoaderImpl::URLLoaderImpl(NetworkContext* context,
|
| + mojom::URLLoaderRequest url_loader_request,
|
| + const ResourceRequest& request,
|
| + mojom::URLLoaderClientPtr url_loader_client)
|
| + : context_(context),
|
| + connected_(true),
|
| + binding_(this, std::move(url_loader_request)),
|
| + url_loader_client_(std::move(url_loader_client)),
|
| + writable_handle_watcher_(FROM_HERE,
|
| + mojo::SimpleWatcher::ArmingPolicy::MANUAL),
|
| + peer_closed_handle_watcher_(FROM_HERE,
|
| + mojo::SimpleWatcher::ArmingPolicy::MANUAL),
|
| + weak_ptr_factory_(this) {
|
| + binding_.set_connection_error_handler(
|
| + base::Bind(&URLLoaderImpl::OnConnectionError, base::Unretained(this)));
|
| +
|
| + url_request_ = context_->url_request_context()->CreateRequest(
|
| + GURL(request.url), net::DEFAULT_PRIORITY, this);
|
| + url_request_->set_method(request.method);
|
| +
|
| + const Referrer referrer(request.referrer, request.referrer_policy);
|
| + Referrer::SetReferrerForRequest(url_request_.get(), referrer);
|
| +
|
| + net::HttpRequestHeaders headers;
|
| + headers.AddHeadersFromString(request.headers);
|
| + url_request_->SetExtraRequestHeaders(headers);
|
| +
|
| + // Resolve elements from request_body and prepare upload data.
|
| + if (request.request_body.get()) {
|
| + scoped_refptr<base::SequencedTaskRunner> task_runner =
|
| + base::CreateSequencedTaskRunnerWithTraits(
|
| + base::TaskTraits().MayBlock().WithPriority(
|
| + base::TaskPriority::USER_VISIBLE));
|
| + url_request_->set_upload(
|
| + CreateUploadDataStream(request.request_body.get(), task_runner.get()));
|
| + }
|
| +
|
| + int load_flags = BuildLoadFlagsForRequest(request, false);
|
| + url_request_->SetLoadFlags(load_flags);
|
| +
|
| + url_request_->Start();
|
| +}
|
| +
|
| +URLLoaderImpl::~URLLoaderImpl() {}
|
| +
|
| +void URLLoaderImpl::Cleanup() {
|
| + // The associated network context is going away and we have to destroy
|
| + // net::URLRequest hold by this loader.
|
| + delete this;
|
| +}
|
| +
|
| +void URLLoaderImpl::FollowRedirect() {
|
| + if (!url_request_) {
|
| + NotifyCompleted(net::ERR_UNEXPECTED);
|
| + return;
|
| + }
|
| +
|
| + url_request_->FollowDeferredRedirect();
|
| +}
|
| +
|
| +void URLLoaderImpl::SetPriority(net::RequestPriority priority,
|
| + int32_t intra_priority_value) {
|
| + NOTIMPLEMENTED();
|
| +}
|
| +
|
| +void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request,
|
| + const net::RedirectInfo& redirect_info,
|
| + bool* defer_redirect) {
|
| + DCHECK(url_request == url_request_.get());
|
| + DCHECK(url_request->status().is_success());
|
| +
|
| + // Send the redirect response to the client, allowing them to inspect it and
|
| + // optionally follow the redirect.
|
| + *defer_redirect = true;
|
| +
|
| + scoped_refptr<ResourceResponse> response = new ResourceResponse();
|
| + PopulateResourceResponse(url_request_.get(), response.get());
|
| +
|
| + url_loader_client_->OnReceiveRedirect(redirect_info, response->head);
|
| +}
|
| +
|
| +void URLLoaderImpl::OnResponseStarted(net::URLRequest* url_request) {
|
| + DCHECK(url_request == url_request_.get());
|
| +
|
| + // TODO: Add support for optional MIME sniffing.
|
| +
|
| + scoped_refptr<ResourceResponse> response = new ResourceResponse();
|
| + PopulateResourceResponse(url_request_.get(), response.get());
|
| +
|
| + mojom::DownloadedTempFilePtr downloaded_file_ptr;
|
| + url_loader_client_->OnReceiveResponse(response->head,
|
| + std::move(downloaded_file_ptr));
|
| +
|
| + net::IOBufferWithSize* metadata = url_request->response_info().metadata.get();
|
| + if (metadata) {
|
| + const uint8_t* data = reinterpret_cast<const uint8_t*>(metadata->data());
|
| +
|
| + url_loader_client_->OnReceiveCachedMetadata(
|
| + std::vector<uint8_t>(data, data + metadata->size()));
|
| + }
|
| +
|
| + MojoCreateDataPipeOptions options;
|
| + options.struct_size = sizeof(MojoCreateDataPipeOptions);
|
| + options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE;
|
| + options.element_num_bytes = 1;
|
| + options.capacity_num_bytes = kDefaultAllocationSize;
|
| + mojo::DataPipe data_pipe(options);
|
| +
|
| + DCHECK(data_pipe.producer_handle.is_valid());
|
| + DCHECK(data_pipe.consumer_handle.is_valid());
|
| +
|
| + response_body_stream_ = std::move(data_pipe.producer_handle);
|
| + response_body_consumer_handle_ = std::move(data_pipe.consumer_handle);
|
| + peer_closed_handle_watcher_.Watch(
|
| + response_body_stream_.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
|
| + base::Bind(&URLLoaderImpl::OnResponseBodyStreamClosed,
|
| + base::Unretained(this)));
|
| + peer_closed_handle_watcher_.ArmOrNotify();
|
| +
|
| + writable_handle_watcher_.Watch(
|
| + response_body_stream_.get(), MOJO_HANDLE_SIGNAL_WRITABLE,
|
| + base::Bind(&URLLoaderImpl::OnResponseBodyStreamReady,
|
| + base::Unretained(this)));
|
| +
|
| + // Start reading...
|
| + ReadMore();
|
| +}
|
| +
|
| +void URLLoaderImpl::ReadMore() {
|
| + DCHECK(!pending_write_.get());
|
| +
|
| + uint32_t num_bytes;
|
| + // TODO: we should use the abstractions in MojoAsyncResourceHandler.
|
| + MojoResult result = NetToMojoPendingBuffer::BeginWrite(
|
| + &response_body_stream_, &pending_write_, &num_bytes);
|
| + if (result == MOJO_RESULT_SHOULD_WAIT) {
|
| + // The pipe is full. We need to wait for it to have more space.
|
| + writable_handle_watcher_.ArmOrNotify();
|
| + return;
|
| + } else if (result != MOJO_RESULT_OK) {
|
| + // The response body stream is in a bad state. Bail.
|
| + // TODO: How should this be communicated to our client?
|
| + writable_handle_watcher_.Cancel();
|
| + response_body_stream_.reset();
|
| + DeleteIfNeeded();
|
| + return;
|
| + }
|
| +
|
| + CHECK_GT(static_cast<uint32_t>(std::numeric_limits<int>::max()), num_bytes);
|
| + scoped_refptr<net::IOBuffer> buf(new NetToMojoIOBuffer(pending_write_.get()));
|
| + int bytes_read;
|
| + url_request_->Read(buf.get(), static_cast<int>(num_bytes), &bytes_read);
|
| + if (url_request_->status().is_io_pending()) {
|
| + // Wait for OnReadCompleted.
|
| + } else if (url_request_->status().is_success() && bytes_read > 0) {
|
| + SendDataPipeIfNecessary();
|
| + DidRead(static_cast<uint32_t>(bytes_read), true);
|
| + } else {
|
| + NotifyCompleted(net::OK);
|
| + writable_handle_watcher_.Cancel();
|
| + pending_write_->Complete(0);
|
| + pending_write_ = nullptr; // This closes the data pipe.
|
| + DeleteIfNeeded();
|
| + return;
|
| + }
|
| +}
|
| +
|
| +void URLLoaderImpl::DidRead(uint32_t num_bytes, bool completed_synchronously) {
|
| + DCHECK(url_request_->status().is_success());
|
| + response_body_stream_ = pending_write_->Complete(num_bytes);
|
| + pending_write_ = nullptr;
|
| + if (completed_synchronously) {
|
| + base::ThreadTaskRunnerHandle::Get()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&URLLoaderImpl::ReadMore, weak_ptr_factory_.GetWeakPtr()));
|
| + } else {
|
| + ReadMore();
|
| + }
|
| +}
|
| +
|
| +void URLLoaderImpl::OnReadCompleted(net::URLRequest* url_request,
|
| + int bytes_read) {
|
| + DCHECK(url_request == url_request_.get());
|
| +
|
| + if (!url_request->status().is_success()) {
|
| + writable_handle_watcher_.Cancel();
|
| + pending_write_ = nullptr; // This closes the data pipe.
|
| + DeleteIfNeeded();
|
| + return;
|
| + }
|
| +
|
| + SendDataPipeIfNecessary();
|
| +
|
| + DidRead(static_cast<uint32_t>(bytes_read), false);
|
| +}
|
| +
|
| +void URLLoaderImpl::NotifyCompleted(int error_code) {
|
| + ResourceRequestCompletionStatus request_complete_data;
|
| + request_complete_data.error_code = error_code;
|
| + request_complete_data.exists_in_cache =
|
| + url_request_->response_info().was_cached;
|
| + request_complete_data.completion_time = base::TimeTicks::Now();
|
| + request_complete_data.encoded_data_length =
|
| + url_request_->GetTotalReceivedBytes();
|
| + request_complete_data.encoded_body_length = url_request_->GetRawBodyBytes();
|
| +
|
| + url_loader_client_->OnComplete(request_complete_data);
|
| + DeleteIfNeeded();
|
| +}
|
| +
|
| +void URLLoaderImpl::SendDataPipeIfNecessary() {
|
| + if (response_body_consumer_handle_.is_valid()) {
|
| + // Send the data pipe on the first OnReadCompleted call.
|
| + url_loader_client_->OnStartLoadingResponseBody(
|
| + std::move(response_body_consumer_handle_));
|
| + }
|
| +}
|
| +
|
| +void URLLoaderImpl::OnConnectionError() {
|
| + connected_ = false;
|
| + DeleteIfNeeded();
|
| +}
|
| +
|
| +void URLLoaderImpl::OnResponseBodyStreamClosed(MojoResult result) {
|
| + url_request_.reset();
|
| + response_body_stream_.reset();
|
| + pending_write_ = nullptr;
|
| + DeleteIfNeeded();
|
| +}
|
| +
|
| +void URLLoaderImpl::OnResponseBodyStreamReady(MojoResult result) {
|
| + // TODO: Handle a bad |result| value.
|
| + DCHECK_EQ(result, MOJO_RESULT_OK);
|
| + ReadMore();
|
| +}
|
| +
|
| +void URLLoaderImpl::DeleteIfNeeded() {
|
| + bool has_data_pipe = pending_write_.get() || response_body_stream_.is_valid();
|
| + if (!connected_ && !has_data_pipe)
|
| + delete this;
|
| +}
|
| +
|
| +} // namespace content
|
|
|