1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/net/view_http_cache_job_factory.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/callback.h"
10#include "base/compiler_specific.h"
11#include "base/memory/weak_ptr.h"
12#include "base/message_loop/message_loop.h"
13#include "base/strings/string_util.h"
14#include "content/public/common/url_constants.h"
15#include "net/base/completion_callback.h"
16#include "net/base/net_errors.h"
17#include "net/url_request/url_request.h"
18#include "net/url_request/url_request_simple_job.h"
19#include "net/url_request/view_cache_helper.h"
20
21namespace content {
22namespace {
23
24// A job subclass that dumps an HTTP cache entry.
25class ViewHttpCacheJob : public net::URLRequestJob {
26 public:
27  ViewHttpCacheJob(net::URLRequest* request,
28                   net::NetworkDelegate* network_delegate)
29      : net::URLRequestJob(request, network_delegate),
30        core_(new Core),
31        callback_(base::Bind(&ViewHttpCacheJob::OnStartCompleted,
32                             base::Unretained(this))),
33        weak_factory_(this) {
34  }
35
36  // net::URLRequestJob implementation.
37  virtual void Start() OVERRIDE;
38  virtual void Kill() OVERRIDE;
39  virtual bool GetMimeType(std::string* mime_type) const OVERRIDE{
40    return core_->GetMimeType(mime_type);
41  }
42  virtual bool GetCharset(std::string* charset) OVERRIDE{
43    return core_->GetCharset(charset);
44  }
45  virtual bool ReadRawData(net::IOBuffer* buf,
46                           int buf_size, int *bytes_read) OVERRIDE{
47    return core_->ReadRawData(buf, buf_size, bytes_read);
48  }
49
50 private:
51  class Core : public base::RefCounted<Core> {
52   public:
53    Core()
54        : data_offset_(0),
55          callback_(base::Bind(&Core::OnIOComplete, this)) {
56    }
57
58    int Start(const net::URLRequest& request, const base::Closure& callback);
59
60    // Prevents it from invoking its callback. It will self-delete.
61    void Orphan() {
62      user_callback_.Reset();
63    }
64
65    bool GetMimeType(std::string* mime_type) const;
66    bool GetCharset(std::string* charset);
67    bool ReadRawData(net::IOBuffer* buf, int buf_size, int *bytes_read);
68
69   private:
70    friend class base::RefCounted<Core>;
71
72    ~Core() {}
73
74    // Called when ViewCacheHelper completes the operation.
75    void OnIOComplete(int result);
76
77    std::string data_;
78    int data_offset_;
79    net::ViewCacheHelper cache_helper_;
80    net::CompletionCallback callback_;
81    base::Closure user_callback_;
82
83    DISALLOW_COPY_AND_ASSIGN(Core);
84  };
85
86  virtual ~ViewHttpCacheJob() {}
87
88  void StartAsync();
89  void OnStartCompleted();
90
91  scoped_refptr<Core> core_;
92  base::Closure callback_;
93
94  base::WeakPtrFactory<ViewHttpCacheJob> weak_factory_;
95
96  DISALLOW_COPY_AND_ASSIGN(ViewHttpCacheJob);
97};
98
99void ViewHttpCacheJob::Start() {
100  base::MessageLoop::current()->PostTask(
101      FROM_HERE,
102      base::Bind(&ViewHttpCacheJob::StartAsync, weak_factory_.GetWeakPtr()));
103}
104
105void ViewHttpCacheJob::Kill() {
106  weak_factory_.InvalidateWeakPtrs();
107  if (core_.get()) {
108    core_->Orphan();
109    core_ = NULL;
110  }
111  net::URLRequestJob::Kill();
112}
113
114void ViewHttpCacheJob::StartAsync() {
115  DCHECK(request());
116
117  if (!request())
118    return;
119
120  int rv = core_->Start(*request(), callback_);
121  if (rv != net::ERR_IO_PENDING) {
122    DCHECK_EQ(net::OK, rv);
123    OnStartCompleted();
124  }
125}
126
127void ViewHttpCacheJob::OnStartCompleted() {
128  NotifyHeadersComplete();
129}
130
131int ViewHttpCacheJob::Core::Start(const net::URLRequest& request,
132                                  const base::Closure& callback) {
133  DCHECK(!callback.is_null());
134  DCHECK(user_callback_.is_null());
135
136  AddRef();  // Released on OnIOComplete().
137  std::string cache_key =
138      request.url().spec().substr(strlen(kChromeUINetworkViewCacheURL));
139
140  int rv;
141  if (cache_key.empty()) {
142    rv = cache_helper_.GetContentsHTML(request.context(),
143                                       kChromeUINetworkViewCacheURL,
144                                       &data_, callback_);
145  } else {
146    rv = cache_helper_.GetEntryInfoHTML(cache_key, request.context(),
147                                        &data_, callback_);
148  }
149
150  if (rv == net::ERR_IO_PENDING)
151    user_callback_ = callback;
152
153  return rv;
154}
155
156bool ViewHttpCacheJob::Core::GetMimeType(std::string* mime_type) const {
157  mime_type->assign("text/html");
158  return true;
159}
160
161bool ViewHttpCacheJob::Core::GetCharset(std::string* charset) {
162  charset->assign("UTF-8");
163  return true;
164}
165
166bool ViewHttpCacheJob::Core::ReadRawData(net::IOBuffer* buf,
167                                         int buf_size,
168                                         int* bytes_read) {
169  DCHECK(bytes_read);
170  int remaining = static_cast<int>(data_.size()) - data_offset_;
171  if (buf_size > remaining)
172    buf_size = remaining;
173  memcpy(buf->data(), data_.data() + data_offset_, buf_size);
174  data_offset_ += buf_size;
175  *bytes_read = buf_size;
176  return true;
177}
178
179void ViewHttpCacheJob::Core::OnIOComplete(int result) {
180  DCHECK_EQ(net::OK, result);
181
182  if (!user_callback_.is_null())
183    user_callback_.Run();
184
185  // We may be holding the last reference to this job. Do not access |this|
186  // after Release().
187  Release();  // Acquired on Start().
188}
189
190}  // namespace.
191
192// Static.
193bool ViewHttpCacheJobFactory::IsSupportedURL(const GURL& url) {
194  return url.SchemeIs(kChromeUIScheme) &&
195         url.host() == kChromeUINetworkViewCacheHost;
196}
197
198// Static.
199net::URLRequestJob* ViewHttpCacheJobFactory::CreateJobForRequest(
200    net::URLRequest* request, net::NetworkDelegate* network_delegate) {
201  return new ViewHttpCacheJob(request, network_delegate);
202}
203
204}  // namespace content
205