| // Copyright (c) 2012 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/net/view_http_cache_job_factory.h" |
| |
| #include <stddef.h> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/callback.h" |
| #include "base/compiler_specific.h" |
| #include "base/location.h" |
| #include "base/macros.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/string_util.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "content/public/common/url_constants.h" |
| #include "net/base/completion_callback.h" |
| #include "net/base/net_errors.h" |
| #include "net/url_request/url_request.h" |
| #include "net/url_request/url_request_simple_job.h" |
| #include "net/url_request/view_cache_helper.h" |
| |
| namespace content { |
| namespace { |
| |
| // A job subclass that dumps an HTTP cache entry. |
| class ViewHttpCacheJob : public net::URLRequestJob { |
| public: |
| ViewHttpCacheJob(net::URLRequest* request, |
| net::NetworkDelegate* network_delegate) |
| : net::URLRequestJob(request, network_delegate), |
| core_(new Core), |
| callback_(base::Bind(&ViewHttpCacheJob::OnStartCompleted, |
| base::Unretained(this))), |
| weak_factory_(this) { |
| } |
| |
| // net::URLRequestJob implementation. |
| void Start() override; |
| void Kill() override; |
| bool GetMimeType(std::string* mime_type) const override { |
| return core_->GetMimeType(mime_type); |
| } |
| bool GetCharset(std::string* charset) override { |
| return core_->GetCharset(charset); |
| } |
| int ReadRawData(net::IOBuffer* buf, int buf_size) override { |
| return core_->ReadRawData(buf, buf_size); |
| } |
| |
| private: |
| class Core : public base::RefCounted<Core> { |
| public: |
| Core() |
| : data_offset_(0), |
| callback_(base::Bind(&Core::OnIOComplete, base::Unretained(this))) {} |
| |
| int Start(const net::URLRequest& request, const base::Closure& callback); |
| |
| // Prevents it from invoking its callback. It will self-delete. |
| void Orphan() { |
| user_callback_.Reset(); |
| } |
| |
| bool GetMimeType(std::string* mime_type) const; |
| bool GetCharset(std::string* charset); |
| int ReadRawData(net::IOBuffer* buf, int buf_size); |
| |
| private: |
| friend class base::RefCounted<Core>; |
| |
| ~Core() {} |
| |
| // Called when ViewCacheHelper completes the operation. |
| void OnIOComplete(int result); |
| |
| std::string data_; |
| size_t data_offset_; |
| net::ViewCacheHelper cache_helper_; |
| net::CompletionCallback callback_; |
| base::Closure user_callback_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Core); |
| }; |
| |
| ~ViewHttpCacheJob() override {} |
| |
| void StartAsync(); |
| void OnStartCompleted(); |
| |
| scoped_refptr<Core> core_; |
| base::Closure callback_; |
| |
| base::WeakPtrFactory<ViewHttpCacheJob> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ViewHttpCacheJob); |
| }; |
| |
| void ViewHttpCacheJob::Start() { |
| base::ThreadTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, base::BindOnce(&ViewHttpCacheJob::StartAsync, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void ViewHttpCacheJob::Kill() { |
| weak_factory_.InvalidateWeakPtrs(); |
| if (core_.get()) { |
| core_->Orphan(); |
| core_ = nullptr; |
| } |
| net::URLRequestJob::Kill(); |
| } |
| |
| void ViewHttpCacheJob::StartAsync() { |
| DCHECK(request()); |
| |
| if (!request()) |
| return; |
| |
| int rv = core_->Start(*request(), callback_); |
| if (rv != net::ERR_IO_PENDING) { |
| DCHECK_EQ(net::OK, rv); |
| OnStartCompleted(); |
| } |
| } |
| |
| void ViewHttpCacheJob::OnStartCompleted() { |
| NotifyHeadersComplete(); |
| } |
| |
| int ViewHttpCacheJob::Core::Start(const net::URLRequest& request, |
| const base::Closure& callback) { |
| DCHECK(!callback.is_null()); |
| DCHECK(user_callback_.is_null()); |
| |
| AddRef(); // Released on OnIOComplete(). |
| std::string cache_key = |
| request.url().spec().substr(strlen(kChromeUINetworkViewCacheURL)); |
| |
| int rv; |
| if (cache_key.empty()) { |
| rv = cache_helper_.GetContentsHTML(request.context(), |
| kChromeUINetworkViewCacheURL, |
| &data_, callback_); |
| } else { |
| rv = cache_helper_.GetEntryInfoHTML(cache_key, request.context(), |
| &data_, callback_); |
| } |
| |
| if (rv == net::ERR_IO_PENDING) |
| user_callback_ = callback; |
| |
| return rv; |
| } |
| |
| bool ViewHttpCacheJob::Core::GetMimeType(std::string* mime_type) const { |
| mime_type->assign("text/html"); |
| return true; |
| } |
| |
| bool ViewHttpCacheJob::Core::GetCharset(std::string* charset) { |
| charset->assign("UTF-8"); |
| return true; |
| } |
| |
| int ViewHttpCacheJob::Core::ReadRawData(net::IOBuffer* buf, int buf_size) { |
| DCHECK_LE(data_offset_, data_.size()); |
| int remaining = base::checked_cast<int>(data_.size() - data_offset_); |
| if (buf_size > remaining) |
| buf_size = remaining; |
| memcpy(buf->data(), data_.data() + data_offset_, buf_size); |
| data_offset_ += buf_size; |
| return buf_size; |
| } |
| |
| void ViewHttpCacheJob::Core::OnIOComplete(int result) { |
| DCHECK_EQ(net::OK, result); |
| |
| if (!user_callback_.is_null()) |
| user_callback_.Run(); |
| |
| // We may be holding the last reference to this job. If it's deleted |
| // synchronously then ViewCacheHelper would have a UaF. |
| base::ThreadTaskRunnerHandle::Get()->ReleaseSoon(FROM_HERE, this); |
| } |
| |
| } // namespace. |
| |
| // Static. |
| bool ViewHttpCacheJobFactory::IsSupportedURL(const GURL& url) { |
| return url.SchemeIs(kChromeUIScheme) && |
| url.host_piece() == kChromeUINetworkViewCacheHost; |
| } |
| |
| // Static. |
| net::URLRequestJob* ViewHttpCacheJobFactory::CreateJobForRequest( |
| net::URLRequest* request, net::NetworkDelegate* network_delegate) { |
| return new ViewHttpCacheJob(request, network_delegate); |
| } |
| |
| } // namespace content |