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
HexDump(const char * buf,size_t buf_len,std::string * result)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
FormatEntryInfo(disk_cache::Entry * entry,const std::string & url_prefix)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
ViewCacheHelper()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
~ViewCacheHelper()92 ViewCacheHelper::~ViewCacheHelper() {
93 if (entry_)
94 entry_->Close();
95
96 // Cancel any pending entry callback.
97 entry_callback_->Cancel();
98 }
99
GetEntryInfoHTML(const std::string & key,URLRequestContext * context,std::string * out,CompletionCallback * callback)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
GetContentsHTML(URLRequestContext * context,const std::string & url_prefix,std::string * out,CompletionCallback * callback)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
GetInfoHTML(const std::string & key,URLRequestContext * context,const std::string & url_prefix,std::string * out,CompletionCallback * callback)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
DoCallback(int rv)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
HandleResult(int rv)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
DoLoop(int result)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
DoGetBackend()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
DoGetBackendComplete(int result)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
DoOpenNextEntry()241 int ViewCacheHelper::DoOpenNextEntry() {
242 next_state_ = STATE_OPEN_NEXT_ENTRY_COMPLETE;
243 return disk_cache_->OpenNextEntry(&iter_, &entry_, &cache_callback_);
244 }
245
DoOpenNextEntryComplete(int result)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
DoOpenEntry()261 int ViewCacheHelper::DoOpenEntry() {
262 next_state_ = STATE_OPEN_ENTRY_COMPLETE;
263 return disk_cache_->OpenEntry(key_, &entry_, &cache_callback_);
264 }
265
DoOpenEntryComplete(int result)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
DoReadResponse()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
DoReadResponseComplete(int result)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
DoReadData()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
DoReadDataComplete(int result)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
OnIOComplete(int result)351 void ViewCacheHelper::OnIOComplete(int result) {
352 DoLoop(result);
353 }
354
355 } // namespace net.
356