| OLD | NEW |
| (Empty) |
| 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/profiler/scoped_tracker.h" | |
| 10 #include "base/strings/stringprintf.h" | |
| 11 #include "net/base/escape.h" | |
| 12 #include "net/base/io_buffer.h" | |
| 13 #include "net/base/net_errors.h" | |
| 14 #include "net/disk_cache/disk_cache.h" | |
| 15 #include "net/http/http_cache.h" | |
| 16 #include "net/http/http_response_headers.h" | |
| 17 #include "net/http/http_response_info.h" | |
| 18 #include "net/url_request/url_request_context.h" | |
| 19 | |
| 20 #define VIEW_CACHE_HEAD \ | |
| 21 "<html><meta charset=\"utf-8\">" \ | |
| 22 "<meta http-equiv=\"Content-Security-Policy\" " \ | |
| 23 " content=\"object-src 'none'; script-src 'none' 'unsafe-eval'\">" \ | |
| 24 "<body><table>" | |
| 25 | |
| 26 #define VIEW_CACHE_TAIL \ | |
| 27 "</table></body></html>" | |
| 28 | |
| 29 namespace net { | |
| 30 | |
| 31 namespace { | |
| 32 | |
| 33 std::string FormatEntryInfo(disk_cache::Entry* entry, | |
| 34 const std::string& url_prefix) { | |
| 35 std::string key = entry->GetKey(); | |
| 36 GURL url = GURL(url_prefix + key); | |
| 37 std::string row = | |
| 38 "<tr><td><a href=\"" + url.spec() + "\">" + EscapeForHTML(key) + | |
| 39 "</a></td></tr>"; | |
| 40 return row; | |
| 41 } | |
| 42 | |
| 43 } // namespace. | |
| 44 | |
| 45 ViewCacheHelper::ViewCacheHelper() | |
| 46 : context_(NULL), | |
| 47 disk_cache_(NULL), | |
| 48 entry_(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 if (!iter_) | |
| 246 iter_ = disk_cache_->CreateIterator(); | |
| 247 return | |
| 248 iter_->OpenNextEntry(&entry_, base::Bind(&ViewCacheHelper::OnIOComplete, | |
| 249 base::Unretained(this))); | |
| 250 } | |
| 251 | |
| 252 int ViewCacheHelper::DoOpenNextEntryComplete(int result) { | |
| 253 if (result == ERR_FAILED) { | |
| 254 data_->append(VIEW_CACHE_TAIL); | |
| 255 return OK; | |
| 256 } | |
| 257 | |
| 258 DCHECK_EQ(OK, result); | |
| 259 data_->append(FormatEntryInfo(entry_, url_prefix_)); | |
| 260 entry_->Close(); | |
| 261 entry_ = NULL; | |
| 262 | |
| 263 next_state_ = STATE_OPEN_NEXT_ENTRY; | |
| 264 return OK; | |
| 265 } | |
| 266 | |
| 267 int ViewCacheHelper::DoOpenEntry() { | |
| 268 next_state_ = STATE_OPEN_ENTRY_COMPLETE; | |
| 269 return disk_cache_->OpenEntry( | |
| 270 key_, &entry_, | |
| 271 base::Bind(&ViewCacheHelper::OnIOComplete, base::Unretained(this))); | |
| 272 } | |
| 273 | |
| 274 int ViewCacheHelper::DoOpenEntryComplete(int result) { | |
| 275 if (result == ERR_FAILED) { | |
| 276 data_->append("no matching cache entry for: " + EscapeForHTML(key_)); | |
| 277 return OK; | |
| 278 } | |
| 279 | |
| 280 data_->assign(VIEW_CACHE_HEAD); | |
| 281 data_->append(EscapeForHTML(entry_->GetKey())); | |
| 282 next_state_ = STATE_READ_RESPONSE; | |
| 283 return OK; | |
| 284 } | |
| 285 | |
| 286 int ViewCacheHelper::DoReadResponse() { | |
| 287 next_state_ = STATE_READ_RESPONSE_COMPLETE; | |
| 288 buf_len_ = entry_->GetDataSize(0); | |
| 289 if (!buf_len_) | |
| 290 return buf_len_; | |
| 291 | |
| 292 buf_ = new IOBuffer(buf_len_); | |
| 293 return entry_->ReadData( | |
| 294 0, | |
| 295 0, | |
| 296 buf_.get(), | |
| 297 buf_len_, | |
| 298 base::Bind(&ViewCacheHelper::OnIOComplete, weak_factory_.GetWeakPtr())); | |
| 299 } | |
| 300 | |
| 301 int ViewCacheHelper::DoReadResponseComplete(int result) { | |
| 302 if (result && result == buf_len_) { | |
| 303 HttpResponseInfo response; | |
| 304 bool truncated; | |
| 305 if (HttpCache::ParseResponseInfo( | |
| 306 buf_->data(), buf_len_, &response, &truncated) && | |
| 307 response.headers.get()) { | |
| 308 if (truncated) | |
| 309 data_->append("<pre>RESPONSE_INFO_TRUNCATED</pre>"); | |
| 310 | |
| 311 data_->append("<hr><pre>"); | |
| 312 data_->append(EscapeForHTML(response.headers->GetStatusLine())); | |
| 313 data_->push_back('\n'); | |
| 314 | |
| 315 void* iter = NULL; | |
| 316 std::string name, value; | |
| 317 while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) { | |
| 318 data_->append(EscapeForHTML(name)); | |
| 319 data_->append(": "); | |
| 320 data_->append(EscapeForHTML(value)); | |
| 321 data_->push_back('\n'); | |
| 322 } | |
| 323 data_->append("</pre>"); | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 index_ = 0; | |
| 328 next_state_ = STATE_READ_DATA; | |
| 329 return OK; | |
| 330 } | |
| 331 | |
| 332 int ViewCacheHelper::DoReadData() { | |
| 333 data_->append("<hr><pre>"); | |
| 334 | |
| 335 next_state_ = STATE_READ_DATA_COMPLETE; | |
| 336 buf_len_ = entry_->GetDataSize(index_); | |
| 337 if (!buf_len_) | |
| 338 return buf_len_; | |
| 339 | |
| 340 buf_ = new IOBuffer(buf_len_); | |
| 341 return entry_->ReadData( | |
| 342 index_, | |
| 343 0, | |
| 344 buf_.get(), | |
| 345 buf_len_, | |
| 346 base::Bind(&ViewCacheHelper::OnIOComplete, weak_factory_.GetWeakPtr())); | |
| 347 } | |
| 348 | |
| 349 int ViewCacheHelper::DoReadDataComplete(int result) { | |
| 350 if (result && result == buf_len_) { | |
| 351 HexDump(buf_->data(), buf_len_, data_); | |
| 352 } | |
| 353 data_->append("</pre>"); | |
| 354 index_++; | |
| 355 if (index_ < HttpCache::kNumCacheEntryDataIndices) { | |
| 356 next_state_ = STATE_READ_DATA; | |
| 357 } else { | |
| 358 data_->append(VIEW_CACHE_TAIL); | |
| 359 entry_->Close(); | |
| 360 entry_ = NULL; | |
| 361 } | |
| 362 return OK; | |
| 363 } | |
| 364 | |
| 365 void ViewCacheHelper::OnIOComplete(int result) { | |
| 366 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
| 367 tracked_objects::ScopedTracker tracking_profile( | |
| 368 FROM_HERE_WITH_EXPLICIT_FUNCTION("422516 ViewCacheHelper::OnIOComplete")); | |
| 369 | |
| 370 DoLoop(result); | |
| 371 } | |
| 372 | |
| 373 } // namespace net. | |
| OLD | NEW |