1 // Copyright (c) 2011 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/string_util.h" 8 #include "net/base/escape.h" 9 #include "net/base/io_buffer.h" 10 #include "net/base/net_errors.h" 11 #include "net/disk_cache/disk_cache.h" 12 #include "net/http/http_cache.h" 13 #include "net/http/http_response_headers.h" 14 #include "net/http/http_response_info.h" 15 #include "net/url_request/url_request_context.h" 16 17 #define VIEW_CACHE_HEAD \ 18 "<html><body><table>" 19 20 #define VIEW_CACHE_TAIL \ 21 "</table></body></html>" 22 23 namespace net { 24 25 namespace { 26 27 void HexDump(const char *buf, size_t buf_len, std::string* result) { 28 const size_t kMaxRows = 16; 29 int offset = 0; 30 31 const unsigned char *p; 32 while (buf_len) { 33 base::StringAppendF(result, "%08x: ", offset); 34 offset += kMaxRows; 35 36 p = (const unsigned char *) buf; 37 38 size_t i; 39 size_t row_max = std::min(kMaxRows, buf_len); 40 41 // print hex codes: 42 for (i = 0; i < row_max; ++i) 43 base::StringAppendF(result, "%02x ", *p++); 44 for (i = row_max; i < kMaxRows; ++i) 45 result->append(" "); 46 47 // print ASCII glyphs if possible: 48 p = (const unsigned char *) buf; 49 for (i = 0; i < row_max; ++i, ++p) { 50 if (*p < 0x7F && *p > 0x1F) { 51 AppendEscapedCharForHTML(*p, result); 52 } else { 53 result->push_back('.'); 54 } 55 } 56 57 result->push_back('\n'); 58 59 buf += row_max; 60 buf_len -= row_max; 61 } 62 } 63 64 std::string FormatEntryInfo(disk_cache::Entry* entry, 65 const std::string& url_prefix) { 66 std::string key = entry->GetKey(); 67 GURL url = GURL(url_prefix + key); 68 std::string row = 69 "<tr><td><a href=\"" + url.spec() + "\">" + EscapeForHTML(key) + 70 "</a></td></tr>"; 71 return row; 72 } 73 74 } // namespace. 75 76 ViewCacheHelper::ViewCacheHelper() 77 : disk_cache_(NULL), 78 entry_(NULL), 79 iter_(NULL), 80 buf_len_(0), 81 index_(0), 82 data_(NULL), 83 callback_(NULL), 84 next_state_(STATE_NONE), 85 ALLOW_THIS_IN_INITIALIZER_LIST( 86 cache_callback_(this, &ViewCacheHelper::OnIOComplete)), 87 ALLOW_THIS_IN_INITIALIZER_LIST( 88 entry_callback_(new CancelableCompletionCallback<ViewCacheHelper>( 89 this, &ViewCacheHelper::OnIOComplete))) { 90 } 91 92 ViewCacheHelper::~ViewCacheHelper() { 93 if (entry_) 94 entry_->Close(); 95 96 // Cancel any pending entry callback. 97 entry_callback_->Cancel(); 98 } 99 100 int ViewCacheHelper::GetEntryInfoHTML(const std::string& key, 101 URLRequestContext* context, 102 std::string* out, 103 CompletionCallback* callback) { 104 return GetInfoHTML(key, context, std::string(), out, callback); 105 } 106 107 int ViewCacheHelper::GetContentsHTML(URLRequestContext* context, 108 const std::string& url_prefix, 109 std::string* out, 110 CompletionCallback* callback) { 111 return GetInfoHTML(std::string(), context, url_prefix, out, callback); 112 } 113 114 //----------------------------------------------------------------------------- 115 116 int ViewCacheHelper::GetInfoHTML(const std::string& key, 117 URLRequestContext* context, 118 const std::string& url_prefix, 119 std::string* out, 120 CompletionCallback* callback) { 121 DCHECK(!callback_); 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 136 void ViewCacheHelper::DoCallback(int rv) { 137 DCHECK_NE(ERR_IO_PENDING, rv); 138 DCHECK(callback_); 139 140 CompletionCallback* c = callback_; 141 callback_ = NULL; 142 c->Run(rv); 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_) 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(&disk_cache_, &cache_callback_); 221 } 222 223 int ViewCacheHelper::DoGetBackendComplete(int result) { 224 if (result == ERR_FAILED) { 225 data_->append("no disk cache"); 226 return OK; 227 } 228 229 DCHECK_EQ(OK, result); 230 if (key_.empty()) { 231 data_->assign(VIEW_CACHE_HEAD); 232 DCHECK(!iter_); 233 next_state_ = STATE_OPEN_NEXT_ENTRY; 234 return OK; 235 } 236 237 next_state_ = STATE_OPEN_ENTRY; 238 return OK; 239 } 240 241 int ViewCacheHelper::DoOpenNextEntry() { 242 next_state_ = STATE_OPEN_NEXT_ENTRY_COMPLETE; 243 return disk_cache_->OpenNextEntry(&iter_, &entry_, &cache_callback_); 244 } 245 246 int ViewCacheHelper::DoOpenNextEntryComplete(int result) { 247 if (result == ERR_FAILED) { 248 data_->append(VIEW_CACHE_TAIL); 249 return OK; 250 } 251 252 DCHECK_EQ(OK, result); 253 data_->append(FormatEntryInfo(entry_, url_prefix_)); 254 entry_->Close(); 255 entry_ = NULL; 256 257 next_state_ = STATE_OPEN_NEXT_ENTRY; 258 return OK; 259 } 260 261 int ViewCacheHelper::DoOpenEntry() { 262 next_state_ = STATE_OPEN_ENTRY_COMPLETE; 263 return disk_cache_->OpenEntry(key_, &entry_, &cache_callback_); 264 } 265 266 int ViewCacheHelper::DoOpenEntryComplete(int result) { 267 if (result == ERR_FAILED) { 268 data_->append("no matching cache entry for: " + EscapeForHTML(key_)); 269 return OK; 270 } 271 272 data_->assign(VIEW_CACHE_HEAD); 273 data_->append(EscapeForHTML(entry_->GetKey())); 274 next_state_ = STATE_READ_RESPONSE; 275 return OK; 276 } 277 278 int ViewCacheHelper::DoReadResponse() { 279 next_state_ = STATE_READ_RESPONSE_COMPLETE; 280 buf_len_ = entry_->GetDataSize(0); 281 entry_callback_->AddRef(); 282 if (!buf_len_) 283 return buf_len_; 284 285 buf_ = new IOBuffer(buf_len_); 286 return entry_->ReadData(0, 0, buf_, buf_len_, entry_callback_); 287 } 288 289 int ViewCacheHelper::DoReadResponseComplete(int result) { 290 entry_callback_->Release(); 291 if (result && result == buf_len_) { 292 HttpResponseInfo response; 293 bool truncated; 294 if (HttpCache::ParseResponseInfo(buf_->data(), buf_len_, &response, 295 &truncated) && 296 response.headers) { 297 if (truncated) 298 data_->append("<pre>RESPONSE_INFO_TRUNCATED</pre>"); 299 300 data_->append("<hr><pre>"); 301 data_->append(EscapeForHTML(response.headers->GetStatusLine())); 302 data_->push_back('\n'); 303 304 void* iter = NULL; 305 std::string name, value; 306 while (response.headers->EnumerateHeaderLines(&iter, &name, &value)) { 307 data_->append(EscapeForHTML(name)); 308 data_->append(": "); 309 data_->append(EscapeForHTML(value)); 310 data_->push_back('\n'); 311 } 312 data_->append("</pre>"); 313 } 314 } 315 316 index_ = 0; 317 next_state_ = STATE_READ_DATA; 318 return OK; 319 } 320 321 int ViewCacheHelper::DoReadData() { 322 data_->append("<hr><pre>"); 323 324 next_state_ = STATE_READ_DATA_COMPLETE; 325 buf_len_ = entry_->GetDataSize(index_); 326 entry_callback_->AddRef(); 327 if (!buf_len_) 328 return buf_len_; 329 330 buf_ = new IOBuffer(buf_len_); 331 return entry_->ReadData(index_, 0, buf_, buf_len_, entry_callback_); 332 } 333 334 int ViewCacheHelper::DoReadDataComplete(int result) { 335 entry_callback_->Release(); 336 if (result && result == buf_len_) { 337 HexDump(buf_->data(), buf_len_, data_); 338 } 339 data_->append("</pre>"); 340 index_++; 341 if (index_ < HttpCache::kNumCacheEntryDataIndices) { 342 next_state_ = STATE_READ_DATA; 343 } else { 344 data_->append(VIEW_CACHE_TAIL); 345 entry_->Close(); 346 entry_ = NULL; 347 } 348 return OK; 349 } 350 351 void ViewCacheHelper::OnIOComplete(int result) { 352 DoLoop(result); 353 } 354 355 } // namespace net. 356