blob: 8a3eaed0b89a84208ca003970870c0fb7573725b [file] [log] [blame]
// Copyright (c) 2011 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 "net/url_request/view_cache_helper.h"
#include "base/string_util.h"
#include "net/base/escape.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/url_request/url_request_context.h"
#define VIEW_CACHE_HEAD \
"<html><body><table>"
#define VIEW_CACHE_TAIL \
"</table></body></html>"
namespace net {
namespace {
void HexDump(const char *buf, size_t buf_len, std::string* result) {
const size_t kMaxRows = 16;
int offset = 0;
const unsigned char *p;
while (buf_len) {
base::StringAppendF(result, "%08x: ", offset);
offset += kMaxRows;
p = (const unsigned char *) buf;
size_t i;
size_t row_max = std::min(kMaxRows, buf_len);
// print hex codes:
for (i = 0; i < row_max; ++i)
base::StringAppendF(result, "%02x ", *p++);
for (i = row_max; i < kMaxRows; ++i)
result->append(" ");
// print ASCII glyphs if possible:
p = (const unsigned char *) buf;
for (i = 0; i < row_max; ++i, ++p) {
if (*p < 0x7F && *p > 0x1F) {
AppendEscapedCharForHTML(*p, result);
} else {
result->push_back('.');
}
}
result->push_back('\n');
buf += row_max;
buf_len -= row_max;
}
}
std::string FormatEntryInfo(disk_cache::Entry* entry,
const std::string& url_prefix) {
std::string key = entry->GetKey();
GURL url = GURL(url_prefix + key);
std::string row =
"<tr><td><a href=\"" + url.spec() + "\">" + EscapeForHTML(key) +
"</a></td></tr>";
return row;
}
} // namespace.
ViewCacheHelper::ViewCacheHelper()
: disk_cache_(NULL),
entry_(NULL),
iter_(NULL),
buf_len_(0),
index_(0),
data_(NULL),
callback_(NULL),
next_state_(STATE_NONE),
ALLOW_THIS_IN_INITIALIZER_LIST(
cache_callback_(this, &ViewCacheHelper::OnIOComplete)),
ALLOW_THIS_IN_INITIALIZER_LIST(
entry_callback_(new CancelableCompletionCallback<ViewCacheHelper>(
this, &ViewCacheHelper::OnIOComplete))) {
}
ViewCacheHelper::~ViewCacheHelper() {
if (entry_)
entry_->Close();
// Cancel any pending entry callback.
entry_callback_->Cancel();
}
int ViewCacheHelper::GetEntryInfoHTML(const std::string& key,
URLRequestContext* context,
std::string* out,
CompletionCallback* callback) {
return GetInfoHTML(key, context, std::string(), out, callback);
}
int ViewCacheHelper::GetContentsHTML(URLRequestContext* context,
const std::string& url_prefix,
std::string* out,
CompletionCallback* callback) {
return GetInfoHTML(std::string(), context, url_prefix, out, callback);
}
//-----------------------------------------------------------------------------
int ViewCacheHelper::GetInfoHTML(const std::string& key,
URLRequestContext* context,
const std::string& url_prefix,
std::string* out,
CompletionCallback* callback) {
DCHECK(!callback_);
DCHECK(context);
key_ = key;
context_ = context;
url_prefix_ = url_prefix;
data_ = out;
next_state_ = STATE_GET_BACKEND;
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
callback_ = callback;
return rv;
}
void ViewCacheHelper::DoCallback(int rv) {
DCHECK_NE(ERR_IO_PENDING, rv);
DCHECK(callback_);
CompletionCallback* c = callback_;
callback_ = NULL;
c->Run(rv);
}
void ViewCacheHelper::HandleResult(int rv) {
DCHECK_NE(ERR_IO_PENDING, rv);
DCHECK_NE(ERR_FAILED, rv);
context_ = NULL;
if (callback_)
DoCallback(rv);
}
int ViewCacheHelper::DoLoop(int result) {
DCHECK(next_state_ != STATE_NONE);
int rv = result;
do {
State state = next_state_;
next_state_ = STATE_NONE;
switch (state) {
case STATE_GET_BACKEND:
DCHECK_EQ(OK, rv);
rv = DoGetBackend();
break;
case STATE_GET_BACKEND_COMPLETE:
rv = DoGetBackendComplete(rv);
break;
case STATE_OPEN_NEXT_ENTRY:
DCHECK_EQ(OK, rv);
rv = DoOpenNextEntry();
break;
case STATE_OPEN_NEXT_ENTRY_COMPLETE:
rv = DoOpenNextEntryComplete(rv);
break;
case STATE_OPEN_ENTRY:
DCHECK_EQ(OK, rv);
rv = DoOpenEntry();
break;
case STATE_OPEN_ENTRY_COMPLETE:
rv = DoOpenEntryComplete(rv);
break;
case STATE_READ_RESPONSE:
DCHECK_EQ(OK, rv);
rv = DoReadResponse();
break;
case STATE_READ_RESPONSE_COMPLETE:
rv = DoReadResponseComplete(rv);
break;
case STATE_READ_DATA:
DCHECK_EQ(OK, rv);
rv = DoReadData();
break;
case STATE_READ_DATA_COMPLETE:
rv = DoReadDataComplete(rv);
break;
default:
NOTREACHED() << "bad state";
rv = ERR_FAILED;
break;
}
} while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
if (rv != ERR_IO_PENDING)
HandleResult(rv);
return rv;
}
int ViewCacheHelper::DoGetBackend() {
next_state_ = STATE_GET_BACKEND_COMPLETE;
if (!context_->http_transaction_factory())
return ERR_FAILED;
HttpCache* http_cache = context_->http_transaction_factory()->GetCache();
if (!http_cache)
return ERR_FAILED;
return http_cache->GetBackend(&disk_cache_, &cache_callback_);
}
int ViewCacheHelper::DoGetBackendComplete(int result) {
if (result == ERR_FAILED) {
data_->append("no disk cache");
return OK;
}
DCHECK_EQ(OK, result);
if (key_.empty()) {
data_->assign(VIEW_CACHE_HEAD);
DCHECK(!iter_);
next_state_ = STATE_OPEN_NEXT_ENTRY;
return OK;
}
next_state_ = STATE_OPEN_ENTRY;
return OK;
}
int ViewCacheHelper::DoOpenNextEntry() {
next_state_ = STATE_OPEN_NEXT_ENTRY_COMPLETE;
return disk_cache_->OpenNextEntry(&iter_, &entry_, &cache_callback_);
}
int ViewCacheHelper::DoOpenNextEntryComplete(int result) {
if (result == ERR_FAILED) {
data_->append(VIEW_CACHE_TAIL);
return OK;
}
DCHECK_EQ(OK, result);
data_->append(FormatEntryInfo(entry_, url_prefix_));
entry_->Close();
entry_ = NULL;
next_state_ = STATE_OPEN_NEXT_ENTRY;
return OK;
}
int ViewCacheHelper::DoOpenEntry() {
next_state_ = STATE_OPEN_ENTRY_COMPLETE;
return disk_cache_->OpenEntry(key_, &entry_, &cache_callback_);
}
int ViewCacheHelper::DoOpenEntryComplete(int result) {
if (result == ERR_FAILED) {
data_->append("no matching cache entry for: " + EscapeForHTML(key_));
return OK;
}
data_->assign(VIEW_CACHE_HEAD);
data_->append(EscapeForHTML(entry_->GetKey()));
next_state_ = STATE_READ_RESPONSE;
return OK;
}
int ViewCacheHelper::DoReadResponse() {
next_state_ = STATE_READ_RESPONSE_COMPLETE;
buf_len_ = entry_->GetDataSize(0);
entry_callback_->AddRef();
if (!buf_len_)
return buf_len_;
buf_ = new IOBuffer(buf_len_);
return entry_->ReadData(0, 0, buf_, buf_len_, entry_callback_);
}
int ViewCacheHelper::DoReadResponseComplete(int result) {
entry_callback_->Release();
if (result && result == buf_len_) {
HttpResponseInfo response;
bool truncated;
if (HttpCache::ParseResponseInfo(buf_->data(), buf_len_, &response,
&truncated) &&
response.headers) {
if (truncated)
data_->append("<pre>RESPONSE_INFO_TRUNCATED</pre>");
data_->append("<hr><pre>");
data_->append(EscapeForHTML(response.headers->GetStatusLine()));
data_->push_back('\n');
void* iter = NULL;
std::string name, value;
while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) {
data_->append(EscapeForHTML(name));
data_->append(": ");
data_->append(EscapeForHTML(value));
data_->push_back('\n');
}
data_->append("</pre>");
}
}
index_ = 0;
next_state_ = STATE_READ_DATA;
return OK;
}
int ViewCacheHelper::DoReadData() {
data_->append("<hr><pre>");
next_state_ = STATE_READ_DATA_COMPLETE;
buf_len_ = entry_->GetDataSize(index_);
entry_callback_->AddRef();
if (!buf_len_)
return buf_len_;
buf_ = new IOBuffer(buf_len_);
return entry_->ReadData(index_, 0, buf_, buf_len_, entry_callback_);
}
int ViewCacheHelper::DoReadDataComplete(int result) {
entry_callback_->Release();
if (result && result == buf_len_) {
HexDump(buf_->data(), buf_len_, data_);
}
data_->append("</pre>");
index_++;
if (index_ < HttpCache::kNumCacheEntryDataIndices) {
next_state_ = STATE_READ_DATA;
} else {
data_->append(VIEW_CACHE_TAIL);
entry_->Close();
entry_ = NULL;
}
return OK;
}
void ViewCacheHelper::OnIOComplete(int result) {
DoLoop(result);
}
} // namespace net.