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 28 namespace net { 29 30 namespace { 31 32 std::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 44 ViewCacheHelper::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 56 ViewCacheHelper::~ViewCacheHelper() { 57 if (entry_) 58 entry_->Close(); 59 } 60 61 int 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 68 int 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 76 void 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 117 int 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 137 void 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 145 void 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 153 int 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 210 int 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 225 int 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 243 int 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 250 int 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 265 int 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 272 int 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 284 int 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 299 int 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 330 int 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 347 int 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 363 void ViewCacheHelper::OnIOComplete(int result) { 364 DoLoop(result); 365 } 366 367 } // namespace net. 368