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 "net/url_request/view_cache_helper.h"
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/strings/stringprintf.h"
10#include "net/base/escape.h"
11#include "net/base/io_buffer.h"
12#include "net/base/net_errors.h"
13#include "net/disk_cache/disk_cache.h"
14#include "net/http/http_cache.h"
15#include "net/http/http_response_headers.h"
16#include "net/http/http_response_info.h"
17#include "net/url_request/url_request_context.h"
18
19#define VIEW_CACHE_HEAD \
20  "<html><meta charset=\"utf-8\">" \
21  "<meta http-equiv=\"Content-Security-Policy\" " \
22  "  content=\"object-src 'none'; script-src 'none' 'unsafe-eval'\">" \
23  "<body><table>"
24
25#define VIEW_CACHE_TAIL \
26  "</table></body></html>"
27
28namespace net {
29
30namespace {
31
32std::string FormatEntryInfo(disk_cache::Entry* entry,
33                            const std::string& url_prefix) {
34  std::string key = entry->GetKey();
35  GURL url = GURL(url_prefix + key);
36  std::string row =
37      "<tr><td><a href=\"" + url.spec() + "\">" + EscapeForHTML(key) +
38      "</a></td></tr>";
39  return row;
40}
41
42}  // namespace.
43
44ViewCacheHelper::ViewCacheHelper()
45    : context_(NULL),
46      disk_cache_(NULL),
47      entry_(NULL),
48      buf_len_(0),
49      index_(0),
50      data_(NULL),
51      next_state_(STATE_NONE),
52      weak_factory_(this) {
53}
54
55ViewCacheHelper::~ViewCacheHelper() {
56  if (entry_)
57    entry_->Close();
58}
59
60int ViewCacheHelper::GetEntryInfoHTML(const std::string& key,
61                                      const URLRequestContext* context,
62                                      std::string* out,
63                                      const CompletionCallback& callback) {
64  return GetInfoHTML(key, context, std::string(), out, callback);
65}
66
67int ViewCacheHelper::GetContentsHTML(const URLRequestContext* context,
68                                     const std::string& url_prefix,
69                                     std::string* out,
70                                     const CompletionCallback& callback) {
71  return GetInfoHTML(std::string(), context, url_prefix, out, callback);
72}
73
74// static
75void ViewCacheHelper::HexDump(const char *buf, size_t buf_len,
76                              std::string* result) {
77  const size_t kMaxRows = 16;
78  int offset = 0;
79
80  const unsigned char *p;
81  while (buf_len) {
82    base::StringAppendF(result, "%08x: ", offset);
83    offset += kMaxRows;
84
85    p = (const unsigned char *) buf;
86
87    size_t i;
88    size_t row_max = std::min(kMaxRows, buf_len);
89
90    // print hex codes:
91    for (i = 0; i < row_max; ++i)
92      base::StringAppendF(result, "%02x ", *p++);
93    for (i = row_max; i < kMaxRows; ++i)
94      result->append("   ");
95    result->append(" ");
96
97    // print ASCII glyphs if possible:
98    p = (const unsigned char *) buf;
99    for (i = 0; i < row_max; ++i, ++p) {
100      if (*p < 0x7F && *p > 0x1F) {
101        AppendEscapedCharForHTML(*p, result);
102      } else {
103        result->push_back('.');
104      }
105    }
106
107    result->push_back('\n');
108
109    buf += row_max;
110    buf_len -= row_max;
111  }
112}
113
114//-----------------------------------------------------------------------------
115
116int ViewCacheHelper::GetInfoHTML(const std::string& key,
117                                 const URLRequestContext* context,
118                                 const std::string& url_prefix,
119                                 std::string* out,
120                                 const CompletionCallback& callback) {
121  DCHECK(callback_.is_null());
122  DCHECK(context);
123  key_ = key;
124  context_ = context;
125  url_prefix_ = url_prefix;
126  data_ = out;
127  next_state_ = STATE_GET_BACKEND;
128  int rv = DoLoop(OK);
129
130  if (rv == ERR_IO_PENDING)
131    callback_ = callback;
132
133  return rv;
134}
135
136void ViewCacheHelper::DoCallback(int rv) {
137  DCHECK_NE(ERR_IO_PENDING, rv);
138  DCHECK(!callback_.is_null());
139
140  callback_.Run(rv);
141  callback_.Reset();
142}
143
144void ViewCacheHelper::HandleResult(int rv) {
145  DCHECK_NE(ERR_IO_PENDING, rv);
146  DCHECK_NE(ERR_FAILED, rv);
147  context_ = NULL;
148  if (!callback_.is_null())
149    DoCallback(rv);
150}
151
152int ViewCacheHelper::DoLoop(int result) {
153  DCHECK(next_state_ != STATE_NONE);
154
155  int rv = result;
156  do {
157    State state = next_state_;
158    next_state_ = STATE_NONE;
159    switch (state) {
160      case STATE_GET_BACKEND:
161        DCHECK_EQ(OK, rv);
162        rv = DoGetBackend();
163        break;
164      case STATE_GET_BACKEND_COMPLETE:
165        rv = DoGetBackendComplete(rv);
166        break;
167      case STATE_OPEN_NEXT_ENTRY:
168        DCHECK_EQ(OK, rv);
169        rv = DoOpenNextEntry();
170        break;
171      case STATE_OPEN_NEXT_ENTRY_COMPLETE:
172        rv = DoOpenNextEntryComplete(rv);
173        break;
174      case STATE_OPEN_ENTRY:
175        DCHECK_EQ(OK, rv);
176        rv = DoOpenEntry();
177        break;
178      case STATE_OPEN_ENTRY_COMPLETE:
179        rv = DoOpenEntryComplete(rv);
180        break;
181      case STATE_READ_RESPONSE:
182        DCHECK_EQ(OK, rv);
183        rv = DoReadResponse();
184        break;
185      case STATE_READ_RESPONSE_COMPLETE:
186        rv = DoReadResponseComplete(rv);
187        break;
188      case STATE_READ_DATA:
189        DCHECK_EQ(OK, rv);
190        rv = DoReadData();
191        break;
192      case STATE_READ_DATA_COMPLETE:
193        rv = DoReadDataComplete(rv);
194        break;
195
196      default:
197        NOTREACHED() << "bad state";
198        rv = ERR_FAILED;
199        break;
200    }
201  } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
202
203  if (rv != ERR_IO_PENDING)
204    HandleResult(rv);
205
206  return rv;
207}
208
209int ViewCacheHelper::DoGetBackend() {
210  next_state_ = STATE_GET_BACKEND_COMPLETE;
211
212  if (!context_->http_transaction_factory())
213    return ERR_FAILED;
214
215  HttpCache* http_cache = context_->http_transaction_factory()->GetCache();
216  if (!http_cache)
217    return ERR_FAILED;
218
219  return http_cache->GetBackend(
220      &disk_cache_, base::Bind(&ViewCacheHelper::OnIOComplete,
221                               base::Unretained(this)));
222}
223
224int ViewCacheHelper::DoGetBackendComplete(int result) {
225  if (result == ERR_FAILED) {
226    data_->append("no disk cache");
227    return OK;
228  }
229
230  DCHECK_EQ(OK, result);
231  if (key_.empty()) {
232    data_->assign(VIEW_CACHE_HEAD);
233    DCHECK(!iter_);
234    next_state_ = STATE_OPEN_NEXT_ENTRY;
235    return OK;
236  }
237
238  next_state_ = STATE_OPEN_ENTRY;
239  return OK;
240}
241
242int ViewCacheHelper::DoOpenNextEntry() {
243  next_state_ = STATE_OPEN_NEXT_ENTRY_COMPLETE;
244  if (!iter_)
245    iter_ = disk_cache_->CreateIterator();
246  return
247      iter_->OpenNextEntry(&entry_, base::Bind(&ViewCacheHelper::OnIOComplete,
248                                               base::Unretained(this)));
249}
250
251int ViewCacheHelper::DoOpenNextEntryComplete(int result) {
252  if (result == ERR_FAILED) {
253    data_->append(VIEW_CACHE_TAIL);
254    return OK;
255  }
256
257  DCHECK_EQ(OK, result);
258  data_->append(FormatEntryInfo(entry_, url_prefix_));
259  entry_->Close();
260  entry_ = NULL;
261
262  next_state_ = STATE_OPEN_NEXT_ENTRY;
263  return OK;
264}
265
266int ViewCacheHelper::DoOpenEntry() {
267  next_state_ = STATE_OPEN_ENTRY_COMPLETE;
268  return disk_cache_->OpenEntry(
269      key_, &entry_,
270      base::Bind(&ViewCacheHelper::OnIOComplete, base::Unretained(this)));
271}
272
273int ViewCacheHelper::DoOpenEntryComplete(int result) {
274  if (result == ERR_FAILED) {
275    data_->append("no matching cache entry for: " + EscapeForHTML(key_));
276    return OK;
277  }
278
279  data_->assign(VIEW_CACHE_HEAD);
280  data_->append(EscapeForHTML(entry_->GetKey()));
281  next_state_ = STATE_READ_RESPONSE;
282  return OK;
283}
284
285int ViewCacheHelper::DoReadResponse() {
286  next_state_ = STATE_READ_RESPONSE_COMPLETE;
287  buf_len_ = entry_->GetDataSize(0);
288  if (!buf_len_)
289    return buf_len_;
290
291  buf_ = new IOBuffer(buf_len_);
292  return entry_->ReadData(
293      0,
294      0,
295      buf_.get(),
296      buf_len_,
297      base::Bind(&ViewCacheHelper::OnIOComplete, weak_factory_.GetWeakPtr()));
298}
299
300int ViewCacheHelper::DoReadResponseComplete(int result) {
301  if (result && result == buf_len_) {
302    HttpResponseInfo response;
303    bool truncated;
304    if (HttpCache::ParseResponseInfo(
305            buf_->data(), buf_len_, &response, &truncated) &&
306        response.headers.get()) {
307      if (truncated)
308        data_->append("<pre>RESPONSE_INFO_TRUNCATED</pre>");
309
310      data_->append("<hr><pre>");
311      data_->append(EscapeForHTML(response.headers->GetStatusLine()));
312      data_->push_back('\n');
313
314      void* iter = NULL;
315      std::string name, value;
316      while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) {
317        data_->append(EscapeForHTML(name));
318        data_->append(": ");
319        data_->append(EscapeForHTML(value));
320        data_->push_back('\n');
321      }
322      data_->append("</pre>");
323    }
324  }
325
326  index_ = 0;
327  next_state_ = STATE_READ_DATA;
328  return OK;
329}
330
331int ViewCacheHelper::DoReadData() {
332  data_->append("<hr><pre>");
333
334  next_state_ = STATE_READ_DATA_COMPLETE;
335  buf_len_ = entry_->GetDataSize(index_);
336  if (!buf_len_)
337    return buf_len_;
338
339  buf_ = new IOBuffer(buf_len_);
340  return entry_->ReadData(
341      index_,
342      0,
343      buf_.get(),
344      buf_len_,
345      base::Bind(&ViewCacheHelper::OnIOComplete, weak_factory_.GetWeakPtr()));
346}
347
348int ViewCacheHelper::DoReadDataComplete(int result) {
349  if (result && result == buf_len_) {
350    HexDump(buf_->data(), buf_len_, data_);
351  }
352  data_->append("</pre>");
353  index_++;
354  if (index_ < HttpCache::kNumCacheEntryDataIndices) {
355    next_state_ = STATE_READ_DATA;
356  } else {
357    data_->append(VIEW_CACHE_TAIL);
358    entry_->Close();
359    entry_ = NULL;
360  }
361  return OK;
362}
363
364void ViewCacheHelper::OnIOComplete(int result) {
365  DoLoop(result);
366}
367
368}  // namespace net.
369