1// Copyright (c) 2011 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/string_util.h"
8#include "net/base/escape.h"
9#include "net/base/io_buffer.h"
10#include "net/base/net_errors.h"
11#include "net/disk_cache/disk_cache.h"
12#include "net/http/http_cache.h"
13#include "net/http/http_response_headers.h"
14#include "net/http/http_response_info.h"
15#include "net/url_request/url_request_context.h"
16
17#define VIEW_CACHE_HEAD \
18  "<html><body><table>"
19
20#define VIEW_CACHE_TAIL \
21  "</table></body></html>"
22
23namespace net {
24
25namespace {
26
27void HexDump(const char *buf, size_t buf_len, std::string* result) {
28  const size_t kMaxRows = 16;
29  int offset = 0;
30
31  const unsigned char *p;
32  while (buf_len) {
33    base::StringAppendF(result, "%08x:  ", offset);
34    offset += kMaxRows;
35
36    p = (const unsigned char *) buf;
37
38    size_t i;
39    size_t row_max = std::min(kMaxRows, buf_len);
40
41    // print hex codes:
42    for (i = 0; i < row_max; ++i)
43      base::StringAppendF(result, "%02x  ", *p++);
44    for (i = row_max; i < kMaxRows; ++i)
45      result->append("    ");
46
47    // print ASCII glyphs if possible:
48    p = (const unsigned char *) buf;
49    for (i = 0; i < row_max; ++i, ++p) {
50      if (*p < 0x7F && *p > 0x1F) {
51        AppendEscapedCharForHTML(*p, result);
52      } else {
53        result->push_back('.');
54      }
55    }
56
57    result->push_back('\n');
58
59    buf += row_max;
60    buf_len -= row_max;
61  }
62}
63
64std::string FormatEntryInfo(disk_cache::Entry* entry,
65                            const std::string& url_prefix) {
66  std::string key = entry->GetKey();
67  GURL url = GURL(url_prefix + key);
68  std::string row =
69      "<tr><td><a href=\"" + url.spec() + "\">" + EscapeForHTML(key) +
70      "</a></td></tr>";
71  return row;
72}
73
74}  // namespace.
75
76ViewCacheHelper::ViewCacheHelper()
77    : disk_cache_(NULL),
78      entry_(NULL),
79      iter_(NULL),
80      buf_len_(0),
81      index_(0),
82      data_(NULL),
83      callback_(NULL),
84      next_state_(STATE_NONE),
85      ALLOW_THIS_IN_INITIALIZER_LIST(
86          cache_callback_(this, &ViewCacheHelper::OnIOComplete)),
87      ALLOW_THIS_IN_INITIALIZER_LIST(
88          entry_callback_(new CancelableCompletionCallback<ViewCacheHelper>(
89              this, &ViewCacheHelper::OnIOComplete))) {
90}
91
92ViewCacheHelper::~ViewCacheHelper() {
93  if (entry_)
94    entry_->Close();
95
96  // Cancel any pending entry callback.
97  entry_callback_->Cancel();
98}
99
100int ViewCacheHelper::GetEntryInfoHTML(const std::string& key,
101                                      URLRequestContext* context,
102                                      std::string* out,
103                                      CompletionCallback* callback) {
104  return GetInfoHTML(key, context, std::string(), out, callback);
105}
106
107int ViewCacheHelper::GetContentsHTML(URLRequestContext* context,
108                                     const std::string& url_prefix,
109                                     std::string* out,
110                                     CompletionCallback* callback) {
111  return GetInfoHTML(std::string(), context, url_prefix, out, callback);
112}
113
114//-----------------------------------------------------------------------------
115
116int ViewCacheHelper::GetInfoHTML(const std::string& key,
117                                 URLRequestContext* context,
118                                 const std::string& url_prefix,
119                                 std::string* out,
120                                 CompletionCallback* callback) {
121  DCHECK(!callback_);
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_);
139
140  CompletionCallback* c = callback_;
141  callback_ = NULL;
142  c->Run(rv);
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_)
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(&disk_cache_, &cache_callback_);
221}
222
223int ViewCacheHelper::DoGetBackendComplete(int result) {
224  if (result == ERR_FAILED) {
225    data_->append("no disk cache");
226    return OK;
227  }
228
229  DCHECK_EQ(OK, result);
230  if (key_.empty()) {
231    data_->assign(VIEW_CACHE_HEAD);
232    DCHECK(!iter_);
233    next_state_ = STATE_OPEN_NEXT_ENTRY;
234    return OK;
235  }
236
237  next_state_ = STATE_OPEN_ENTRY;
238  return OK;
239}
240
241int ViewCacheHelper::DoOpenNextEntry() {
242  next_state_ = STATE_OPEN_NEXT_ENTRY_COMPLETE;
243  return disk_cache_->OpenNextEntry(&iter_, &entry_, &cache_callback_);
244}
245
246int ViewCacheHelper::DoOpenNextEntryComplete(int result) {
247  if (result == ERR_FAILED) {
248    data_->append(VIEW_CACHE_TAIL);
249    return OK;
250  }
251
252  DCHECK_EQ(OK, result);
253  data_->append(FormatEntryInfo(entry_, url_prefix_));
254  entry_->Close();
255  entry_ = NULL;
256
257  next_state_ = STATE_OPEN_NEXT_ENTRY;
258  return OK;
259}
260
261int ViewCacheHelper::DoOpenEntry() {
262  next_state_ = STATE_OPEN_ENTRY_COMPLETE;
263  return disk_cache_->OpenEntry(key_, &entry_, &cache_callback_);
264}
265
266int ViewCacheHelper::DoOpenEntryComplete(int result) {
267  if (result == ERR_FAILED) {
268    data_->append("no matching cache entry for: " + EscapeForHTML(key_));
269    return OK;
270  }
271
272  data_->assign(VIEW_CACHE_HEAD);
273  data_->append(EscapeForHTML(entry_->GetKey()));
274  next_state_ = STATE_READ_RESPONSE;
275  return OK;
276}
277
278int ViewCacheHelper::DoReadResponse() {
279  next_state_ = STATE_READ_RESPONSE_COMPLETE;
280  buf_len_ = entry_->GetDataSize(0);
281  entry_callback_->AddRef();
282  if (!buf_len_)
283    return buf_len_;
284
285  buf_ = new IOBuffer(buf_len_);
286  return entry_->ReadData(0, 0, buf_, buf_len_, entry_callback_);
287}
288
289int ViewCacheHelper::DoReadResponseComplete(int result) {
290  entry_callback_->Release();
291  if (result && result == buf_len_) {
292    HttpResponseInfo response;
293    bool truncated;
294    if (HttpCache::ParseResponseInfo(buf_->data(), buf_len_, &response,
295                                          &truncated) &&
296        response.headers) {
297      if (truncated)
298        data_->append("<pre>RESPONSE_INFO_TRUNCATED</pre>");
299
300      data_->append("<hr><pre>");
301      data_->append(EscapeForHTML(response.headers->GetStatusLine()));
302      data_->push_back('\n');
303
304      void* iter = NULL;
305      std::string name, value;
306      while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) {
307        data_->append(EscapeForHTML(name));
308        data_->append(": ");
309        data_->append(EscapeForHTML(value));
310        data_->push_back('\n');
311      }
312      data_->append("</pre>");
313    }
314  }
315
316  index_ = 0;
317  next_state_ = STATE_READ_DATA;
318  return OK;
319}
320
321int ViewCacheHelper::DoReadData() {
322  data_->append("<hr><pre>");
323
324  next_state_ = STATE_READ_DATA_COMPLETE;
325  buf_len_ = entry_->GetDataSize(index_);
326  entry_callback_->AddRef();
327  if (!buf_len_)
328    return buf_len_;
329
330  buf_ = new IOBuffer(buf_len_);
331  return entry_->ReadData(index_, 0, buf_, buf_len_, entry_callback_);
332}
333
334int ViewCacheHelper::DoReadDataComplete(int result) {
335  entry_callback_->Release();
336  if (result && result == buf_len_) {
337    HexDump(buf_->data(), buf_len_, data_);
338  }
339  data_->append("</pre>");
340  index_++;
341  if (index_ < HttpCache::kNumCacheEntryDataIndices) {
342    next_state_ = STATE_READ_DATA;
343  } else {
344    data_->append(VIEW_CACHE_TAIL);
345    entry_->Close();
346    entry_ = NULL;
347  }
348  return OK;
349}
350
351void ViewCacheHelper::OnIOComplete(int result) {
352  DoLoop(result);
353}
354
355}  // namespace net.
356