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