| // Copyright (c) 2012 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 <utility> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/strings/stringprintf.h" |
| #include "net/base/escape.h" |
| #include "net/base/io_buffer.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/request_priority.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><meta charset=\"utf-8\">" \ |
| "<meta http-equiv=\"Content-Security-Policy\" " \ |
| " content=\"object-src 'none'; script-src 'none'\">" \ |
| "<body><table>" |
| |
| #define VIEW_CACHE_TAIL \ |
| "</table></body></html>" |
| |
| namespace net { |
| |
| namespace { |
| |
| 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() |
| : context_(NULL), |
| disk_cache_(NULL), |
| entry_(NULL), |
| buf_len_(0), |
| index_(0), |
| data_(NULL), |
| next_state_(STATE_NONE), |
| weak_factory_(this) { |
| } |
| |
| ViewCacheHelper::~ViewCacheHelper() { |
| if (entry_) |
| entry_->Close(); |
| } |
| |
| int ViewCacheHelper::GetEntryInfoHTML(const std::string& key, |
| const URLRequestContext* context, |
| std::string* out, |
| CompletionOnceCallback callback) { |
| return GetInfoHTML(key, context, std::string(), out, std::move(callback)); |
| } |
| |
| int ViewCacheHelper::GetContentsHTML(const URLRequestContext* context, |
| const std::string& url_prefix, |
| std::string* out, |
| CompletionOnceCallback callback) { |
| return GetInfoHTML(std::string(), context, url_prefix, out, |
| std::move(callback)); |
| } |
| |
| // static |
| void ViewCacheHelper::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(" "); |
| 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; |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| |
| int ViewCacheHelper::GetInfoHTML(const std::string& key, |
| const URLRequestContext* context, |
| const std::string& url_prefix, |
| std::string* out, |
| CompletionOnceCallback callback) { |
| DCHECK(callback_.is_null()); |
| 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_ = std::move(callback); |
| |
| return rv; |
| } |
| |
| void ViewCacheHelper::DoCallback(int rv) { |
| DCHECK_NE(ERR_IO_PENDING, rv); |
| DCHECK(!callback_.is_null()); |
| |
| std::move(callback_).Run(rv); |
| } |
| |
| void ViewCacheHelper::HandleResult(int rv) { |
| DCHECK_NE(ERR_IO_PENDING, rv); |
| DCHECK_NE(ERR_FAILED, rv); |
| context_ = NULL; |
| if (!callback_.is_null()) |
| 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_, |
| base::BindOnce(&ViewCacheHelper::OnIOComplete, base::Unretained(this))); |
| } |
| |
| 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; |
| if (!iter_) |
| iter_ = disk_cache_->CreateIterator(); |
| return |
| iter_->OpenNextEntry(&entry_, base::Bind(&ViewCacheHelper::OnIOComplete, |
| base::Unretained(this))); |
| } |
| |
| 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_, net::HIGHEST, &entry_, |
| base::Bind(&ViewCacheHelper::OnIOComplete, base::Unretained(this))); |
| } |
| |
| 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); |
| if (!buf_len_) |
| return buf_len_; |
| |
| buf_ = base::MakeRefCounted<IOBuffer>(buf_len_); |
| return entry_->ReadData( |
| 0, |
| 0, |
| buf_.get(), |
| buf_len_, |
| base::Bind(&ViewCacheHelper::OnIOComplete, weak_factory_.GetWeakPtr())); |
| } |
| |
| int ViewCacheHelper::DoReadResponseComplete(int result) { |
| if (result && result == buf_len_) { |
| HttpResponseInfo response; |
| bool truncated; |
| if (HttpCache::ParseResponseInfo( |
| buf_->data(), buf_len_, &response, &truncated) && |
| response.headers.get()) { |
| if (truncated) |
| data_->append("<pre>RESPONSE_INFO_TRUNCATED</pre>"); |
| |
| data_->append("<hr><pre>"); |
| data_->append(EscapeForHTML(response.headers->GetStatusLine())); |
| data_->push_back('\n'); |
| |
| size_t iter = 0; |
| 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_); |
| if (!buf_len_) |
| return buf_len_; |
| |
| buf_ = base::MakeRefCounted<IOBuffer>(buf_len_); |
| return entry_->ReadData( |
| index_, |
| 0, |
| buf_.get(), |
| buf_len_, |
| base::Bind(&ViewCacheHelper::OnIOComplete, weak_factory_.GetWeakPtr())); |
| } |
| |
| int ViewCacheHelper::DoReadDataComplete(int result) { |
| 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. |