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