Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1042)

Unified Diff: content/network/url_loader_impl.cc

Issue 2817453002: Bring back the URLLoader from the old Mandoline network service. (Closed)
Patch Set: fix checkdeps Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/network/url_loader_impl.h ('k') | content/network/url_loader_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « content/network/url_loader_impl.h ('k') | content/network/url_loader_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698