1 // Copyright 2016 The Chromium Authors
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 <iostream>
6 #include <memory>
7 #include <unordered_map>
8
9 #include "base/at_exit.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/format_macros.h"
13 #include "base/hash/md5.h"
14 #include "base/logging.h"
15 #include "base/message_loop/message_pump_type.h"
16 #include "base/run_loop.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_piece.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/task/single_thread_task_executor.h"
21 #include "base/task/thread_pool/thread_pool_instance.h"
22 #include "net/base/io_buffer.h"
23 #include "net/base/test_completion_callback.h"
24 #include "net/disk_cache/disk_cache.h"
25 #include "net/disk_cache/disk_cache_test_util.h"
26 #include "net/http/http_cache.h"
27 #include "net/http/http_response_headers.h"
28 #include "net/http/http_response_info.h"
29 #include "net/http/http_util.h"
30
31 using disk_cache::Backend;
32 using disk_cache::BackendResult;
33 using disk_cache::Entry;
34 using disk_cache::EntryResult;
35
36 namespace {
37
38 struct EntryData {
39 std::string url;
40 std::string mime_type;
41 int size;
42 };
43
44 constexpr int kResponseInfoIndex = 0;
45 constexpr int kResponseContentIndex = 1;
46
47 const char* const kCommandNames[] = {
48 "stop", "get_size", "list_keys", "get_stream",
49 "delete_stream", "delete_key", "update_raw_headers", "list_dups",
50 "set_header"};
51
52 // Prints the command line help.
PrintHelp()53 void PrintHelp() {
54 std::cout << "cachetool <cache_path> <cache_backend_type> <subcommand> "
55 << std::endl
56 << std::endl;
57 std::cout << "Available cache backend types: simple, blockfile" << std::endl;
58 std::cout << "Available subcommands:" << std::endl;
59 std::cout << " batch: Starts cachetool to process serialized commands "
60 << "passed down by the standard input and return commands output "
61 << "in the stdout until the stop command is received." << std::endl;
62 std::cout << " delete_key <key>: Delete key from cache." << std::endl;
63 std::cout << " delete_stream <key> <index>: Delete a particular stream of a"
64 << " given key." << std::endl;
65 std::cout << " get_size: Calculate the total size of the cache in bytes."
66 << std::endl;
67 std::cout << " get_stream <key> <index>: Print a particular stream for a"
68 << " given key." << std::endl;
69 std::cout << " list_keys: List all keys in the cache." << std::endl;
70 std::cout << " list_dups: List all resources with duplicate bodies in the "
71 << "cache." << std::endl;
72 std::cout << " update_raw_headers <key>: Update stdin as the key's raw "
73 << "response headers." << std::endl;
74 std::cout << " set_header <key> <name> <value>: Set one of key's raw "
75 << "response headers." << std::endl;
76 std::cout << " stop: Verify that the cache can be opened and return, "
77 << "confirming the cache exists and is of the right type."
78 << std::endl;
79 std::cout << "Expected values of <index> are:" << std::endl;
80 std::cout << " 0 (HTTP response headers)" << std::endl;
81 std::cout << " 1 (transport encoded content)" << std::endl;
82 std::cout << " 2 (compiled content)" << std::endl;
83 }
84
85 // Generic command input/output.
86 class CommandMarshal {
87 public:
CommandMarshal(Backend * cache_backend)88 explicit CommandMarshal(Backend* cache_backend)
89 : cache_backend_(cache_backend) {}
90 virtual ~CommandMarshal() = default;
91
92 // Reads the next command's name to execute.
93 virtual std::string ReadCommandName() = 0;
94
95 // Reads the next parameter as an integer.
96 virtual int ReadInt() = 0;
97
98 // Reads the next parameter as stream index.
ReadStreamIndex()99 int ReadStreamIndex() {
100 if (has_failed())
101 return -1;
102 int index = ReadInt();
103 if (index < 0 || index > 2) {
104 ReturnFailure("Invalid stream index.");
105 return -1;
106 }
107 return index;
108 }
109
110 // Reads the next parameter as a string.
111 virtual std::string ReadString() = 0;
112
113 // Reads the next parameter from stdin as string.
114 virtual std::string ReadBufferedString() = 0;
115
116 // Communicates back an integer.
117 virtual void ReturnInt(int integer) = 0;
118
119 // Communicates back a 64-bit integer.
120 virtual void ReturnInt64(int64_t integer) = 0;
121
122 // Communicates back a string.
123 virtual void ReturnString(const std::string& string) = 0;
124
125 // Communicates back a buffer.
126 virtual void ReturnBuffer(net::GrowableIOBuffer* buffer) = 0;
127
128 // Communicates back command failure.
129 virtual void ReturnFailure(const std::string& error_msg) = 0;
130
131 // Communicates back command success.
ReturnSuccess()132 virtual void ReturnSuccess() { DCHECK(!command_failed_); }
133
134 // Returns whether the command has failed.
has_failed()135 inline bool has_failed() { return command_failed_; }
136
137 // Returns the opened cache backend.
cache_backend()138 Backend* cache_backend() { return cache_backend_; }
139
140 protected:
141 bool command_failed_ = false;
142 Backend* const cache_backend_;
143 };
144
145 // Command line input/output that is user readable.
146 class ProgramArgumentCommandMarshal final : public CommandMarshal {
147 public:
ProgramArgumentCommandMarshal(Backend * cache_backend,base::CommandLine::StringVector args)148 ProgramArgumentCommandMarshal(Backend* cache_backend,
149 base::CommandLine::StringVector args)
150 : CommandMarshal(cache_backend), command_line_args_(args) {}
151
152 // Implements CommandMarshal.
ReadCommandName()153 std::string ReadCommandName() override {
154 if (args_id_ == 0)
155 return ReadString();
156 else if (args_id_ == command_line_args_.size())
157 return "stop";
158 else if (!has_failed())
159 ReturnFailure("Command line arguments too long.");
160 return "";
161 }
162
163 // Implements CommandMarshal.
ReadInt()164 int ReadInt() override {
165 std::string integer_str = ReadString();
166 int integer = -1;
167 if (!base::StringToInt(integer_str, &integer)) {
168 ReturnFailure("Couldn't parse integer.");
169 return 0;
170 }
171 return integer;
172 }
173
174 // Implements CommandMarshal.
ReadString()175 std::string ReadString() override {
176 if (args_id_ < command_line_args_.size())
177 return command_line_args_[args_id_++];
178 if (!has_failed())
179 ReturnFailure("Command line arguments too short.");
180 return "";
181 }
182
183 // Implements CommandMarshal.
ReadBufferedString()184 std::string ReadBufferedString() override {
185 std::ostringstream raw_headers_stream;
186 for (std::string line; std::getline(std::cin, line);)
187 raw_headers_stream << line << std::endl;
188 return raw_headers_stream.str();
189 }
190
191 // Implements CommandMarshal.
ReturnInt(int integer)192 void ReturnInt(int integer) override {
193 DCHECK(!has_failed());
194 std::cout << integer << std::endl;
195 }
196
197 // Implements CommandMarshal.
ReturnInt64(int64_t integer)198 void ReturnInt64(int64_t integer) override {
199 DCHECK(!has_failed());
200 std::cout << integer << std::endl;
201 }
202
203 // Implements CommandMarshal.
ReturnString(const std::string & string)204 void ReturnString(const std::string& string) override {
205 DCHECK(!has_failed());
206 std::cout << string << std::endl;
207 }
208
209 // Implements CommandMarshal.
ReturnBuffer(net::GrowableIOBuffer * buffer)210 void ReturnBuffer(net::GrowableIOBuffer* buffer) override {
211 DCHECK(!has_failed());
212 std::cout.write(buffer->StartOfBuffer(), buffer->offset());
213 }
214
215 // Implements CommandMarshal.
ReturnFailure(const std::string & error_msg)216 void ReturnFailure(const std::string& error_msg) override {
217 DCHECK(!has_failed());
218 std::cerr << error_msg << std::endl;
219 command_failed_ = true;
220 }
221
222 private:
223 const base::CommandLine::StringVector command_line_args_;
224 size_t args_id_ = 0;
225 };
226
227 // Online command input/output that receives pickled commands from stdin and
228 // returns their results back in stdout. Send the stop command to properly exit
229 // cachetool's main loop.
230 class StreamCommandMarshal final : public CommandMarshal {
231 public:
StreamCommandMarshal(Backend * cache_backend)232 explicit StreamCommandMarshal(Backend* cache_backend)
233 : CommandMarshal(cache_backend) {}
234
235 // Implements CommandMarshal.
ReadCommandName()236 std::string ReadCommandName() override {
237 if (has_failed())
238 return "";
239 std::cout.flush();
240 size_t command_id = static_cast<size_t>(std::cin.get());
241 if (command_id >= std::size(kCommandNames)) {
242 ReturnFailure("Unknown command.");
243 return "";
244 }
245 return kCommandNames[command_id];
246 }
247
248 // Implements CommandMarshal.
ReadInt()249 int ReadInt() override {
250 if (has_failed())
251 return -1;
252 int integer = -1;
253 std::cin.read(reinterpret_cast<char*>(&integer), sizeof(integer));
254 return integer;
255 }
256
257 // Implements CommandMarshal.
ReadString()258 std::string ReadString() override {
259 if (has_failed())
260 return "";
261 int string_size = ReadInt();
262 if (string_size <= 0) {
263 if (string_size < 0)
264 ReturnFailure("Size of string is negative.");
265 return "";
266 }
267 std::vector<char> tmp_buffer(string_size + 1);
268 std::cin.read(tmp_buffer.data(), string_size);
269 tmp_buffer[string_size] = 0;
270 return std::string(tmp_buffer.data(), string_size);
271 }
272
273 // Implements CommandMarshal.
ReadBufferedString()274 std::string ReadBufferedString() override { return ReadString(); }
275
276 // Implements CommandMarshal.
ReturnInt(int integer)277 void ReturnInt(int integer) override {
278 DCHECK(!command_failed_);
279 std::cout.write(reinterpret_cast<char*>(&integer), sizeof(integer));
280 }
281
282 // Implements CommandMarshal.
ReturnInt64(int64_t integer)283 void ReturnInt64(int64_t integer) override {
284 DCHECK(!has_failed());
285 std::cout.write(reinterpret_cast<char*>(&integer), sizeof(integer));
286 }
287
288 // Implements CommandMarshal.
ReturnString(const std::string & string)289 void ReturnString(const std::string& string) override {
290 ReturnInt(string.size());
291 std::cout.write(string.c_str(), string.size());
292 }
293
294 // Implements CommandMarshal.
ReturnBuffer(net::GrowableIOBuffer * buffer)295 void ReturnBuffer(net::GrowableIOBuffer* buffer) override {
296 ReturnInt(buffer->offset());
297 std::cout.write(buffer->StartOfBuffer(), buffer->offset());
298 }
299
300 // Implements CommandMarshal.
ReturnFailure(const std::string & error_msg)301 void ReturnFailure(const std::string& error_msg) override {
302 ReturnString(error_msg);
303 command_failed_ = true;
304 }
305
306 // Implements CommandMarshal.
ReturnSuccess()307 void ReturnSuccess() override { ReturnInt(0); }
308 };
309
310 // Gets the cache's size.
GetSize(CommandMarshal * command_marshal)311 void GetSize(CommandMarshal* command_marshal) {
312 net::TestInt64CompletionCallback cb;
313 int64_t rv = command_marshal->cache_backend()->CalculateSizeOfAllEntries(
314 cb.callback());
315 rv = cb.GetResult(rv);
316 if (rv < 0)
317 return command_marshal->ReturnFailure("Couldn't get cache size.");
318 command_marshal->ReturnSuccess();
319 command_marshal->ReturnInt64(rv);
320 }
321
322 // Prints all of a cache's keys to stdout.
ListKeys(CommandMarshal * command_marshal)323 bool ListKeys(CommandMarshal* command_marshal) {
324 std::unique_ptr<Backend::Iterator> entry_iterator =
325 command_marshal->cache_backend()->CreateIterator();
326 TestEntryResultCompletionCallback cb;
327 EntryResult result = entry_iterator->OpenNextEntry(cb.callback());
328 command_marshal->ReturnSuccess();
329 while ((result = cb.GetResult(std::move(result))).net_error() == net::OK) {
330 Entry* entry = result.ReleaseEntry();
331 std::string url = entry->GetKey();
332 command_marshal->ReturnString(url);
333 entry->Close();
334 result = entry_iterator->OpenNextEntry(cb.callback());
335 }
336 command_marshal->ReturnString("");
337 return true;
338 }
339
GetResponseInfoForEntry(disk_cache::Entry * entry,net::HttpResponseInfo * response_info)340 bool GetResponseInfoForEntry(disk_cache::Entry* entry,
341 net::HttpResponseInfo* response_info) {
342 int size = entry->GetDataSize(kResponseInfoIndex);
343 if (size == 0)
344 return false;
345 scoped_refptr<net::IOBuffer> buffer =
346 base::MakeRefCounted<net::IOBufferWithSize>(size);
347 net::TestCompletionCallback cb;
348
349 int bytes_read = 0;
350 while (true) {
351 int rv = entry->ReadData(kResponseInfoIndex, bytes_read, buffer.get(), size,
352 cb.callback());
353 rv = cb.GetResult(rv);
354 if (rv < 0) {
355 entry->Close();
356 return false;
357 }
358
359 if (rv == 0) {
360 bool truncated_response_info = false;
361 if (!net::HttpCache::ParseResponseInfo(
362 buffer->data(), size, response_info, &truncated_response_info)) {
363 return false;
364 }
365 return !truncated_response_info;
366 }
367
368 bytes_read += rv;
369 }
370
371 NOTREACHED();
372 return false;
373 }
374
GetMD5ForResponseBody(disk_cache::Entry * entry)375 std::string GetMD5ForResponseBody(disk_cache::Entry* entry) {
376 if (entry->GetDataSize(kResponseContentIndex) == 0)
377 return "";
378
379 const int kInitBufferSize = 80 * 1024;
380 scoped_refptr<net::IOBuffer> buffer =
381 base::MakeRefCounted<net::IOBufferWithSize>(kInitBufferSize);
382 net::TestCompletionCallback cb;
383
384 base::MD5Context ctx;
385 base::MD5Init(&ctx);
386
387 int bytes_read = 0;
388 while (true) {
389 int rv = entry->ReadData(kResponseContentIndex, bytes_read, buffer.get(),
390 kInitBufferSize, cb.callback());
391 rv = cb.GetResult(rv);
392 if (rv < 0) {
393 entry->Close();
394 return "";
395 }
396
397 if (rv == 0) {
398 base::MD5Digest digest;
399 base::MD5Final(&digest, &ctx);
400 return base::MD5DigestToBase16(digest);
401 }
402
403 bytes_read += rv;
404 base::MD5Update(&ctx, base::StringPiece(buffer->data(), rv));
405 }
406
407 NOTREACHED();
408 return "";
409 }
410
PersistResponseInfo(CommandMarshal * command_marshal,const std::string & key,const net::HttpResponseInfo & response_info)411 void PersistResponseInfo(CommandMarshal* command_marshal,
412 const std::string& key,
413 const net::HttpResponseInfo& response_info) {
414 scoped_refptr<net::PickledIOBuffer> data =
415 base::MakeRefCounted<net::PickledIOBuffer>();
416 response_info.Persist(data->pickle(), false, false);
417 data->Done();
418
419 TestEntryResultCompletionCallback cb_open;
420 EntryResult result = command_marshal->cache_backend()->OpenEntry(
421 key, net::HIGHEST, cb_open.callback());
422 result = cb_open.GetResult(std::move(result));
423 CHECK_EQ(result.net_error(), net::OK);
424 Entry* cache_entry = result.ReleaseEntry();
425
426 int data_len = data->pickle()->size();
427 net::TestCompletionCallback cb;
428 int rv = cache_entry->WriteData(kResponseInfoIndex, 0, data.get(), data_len,
429 cb.callback(), true);
430 if (cb.GetResult(rv) != data_len)
431 return command_marshal->ReturnFailure("Couldn't write headers.");
432 command_marshal->ReturnSuccess();
433 cache_entry->Close();
434 }
435
ListDups(CommandMarshal * command_marshal)436 void ListDups(CommandMarshal* command_marshal) {
437 std::unique_ptr<Backend::Iterator> entry_iterator =
438 command_marshal->cache_backend()->CreateIterator();
439 TestEntryResultCompletionCallback cb;
440 disk_cache::EntryResult result = entry_iterator->OpenNextEntry(cb.callback());
441 command_marshal->ReturnSuccess();
442
443 std::unordered_map<std::string, std::vector<EntryData>> md5_entries;
444
445 int total_entries = 0;
446
447 while ((result = cb.GetResult(std::move(result))).net_error() == net::OK) {
448 Entry* entry = result.ReleaseEntry();
449 total_entries += 1;
450 net::HttpResponseInfo response_info;
451 if (!GetResponseInfoForEntry(entry, &response_info)) {
452 entry->Close();
453 entry = nullptr;
454 result = entry_iterator->OpenNextEntry(cb.callback());
455 continue;
456 }
457
458 std::string hash = GetMD5ForResponseBody(entry);
459 if (hash.empty()) {
460 // Sparse entries and empty bodies are skipped.
461 entry->Close();
462 entry = nullptr;
463 result = entry_iterator->OpenNextEntry(cb.callback());
464 continue;
465 }
466
467 EntryData entry_data;
468
469 entry_data.url = entry->GetKey();
470 entry_data.size = entry->GetDataSize(kResponseContentIndex);
471 if (response_info.headers)
472 response_info.headers->GetMimeType(&entry_data.mime_type);
473
474 auto iter = md5_entries.find(hash);
475 if (iter == md5_entries.end())
476 md5_entries.insert(
477 std::make_pair(hash, std::vector<EntryData>{entry_data}));
478 else
479 iter->second.push_back(entry_data);
480
481 entry->Close();
482 entry = nullptr;
483 result = entry_iterator->OpenNextEntry(cb.callback());
484 }
485
486 // Print the duplicates and collect stats.
487 int total_duped_entries = 0;
488 int64_t total_duped_bytes = 0u;
489 for (const auto& hash_and_entries : md5_entries) {
490 if (hash_and_entries.second.size() == 1)
491 continue;
492
493 int dups = hash_and_entries.second.size() - 1;
494 total_duped_entries += dups;
495 total_duped_bytes += hash_and_entries.second[0].size * dups;
496
497 for (const auto& entry : hash_and_entries.second) {
498 std::string out = base::StringPrintf(
499 "%d, %s, %s", entry.size, entry.url.c_str(), entry.mime_type.c_str());
500 command_marshal->ReturnString(out);
501 }
502 }
503
504 // Print the stats.
505 net::TestInt64CompletionCallback size_cb;
506 int64_t rv = command_marshal->cache_backend()->CalculateSizeOfAllEntries(
507 size_cb.callback());
508 rv = size_cb.GetResult(rv);
509 LOG(ERROR) << "Wasted bytes = " << total_duped_bytes;
510 LOG(ERROR) << "Wasted entries = " << total_duped_entries;
511 LOG(ERROR) << "Total entries = " << total_entries;
512 LOG(ERROR) << "Cache size = " << rv;
513 LOG(ERROR) << "Percentage of cache wasted = " << total_duped_bytes * 100 / rv;
514 }
515
516 // Gets a key's stream to a buffer.
GetStreamForKeyBuffer(CommandMarshal * command_marshal,const std::string & key,int index)517 scoped_refptr<net::GrowableIOBuffer> GetStreamForKeyBuffer(
518 CommandMarshal* command_marshal,
519 const std::string& key,
520 int index) {
521 DCHECK(!command_marshal->has_failed());
522
523 TestEntryResultCompletionCallback cb_open;
524 EntryResult result = command_marshal->cache_backend()->OpenEntry(
525 key, net::HIGHEST, cb_open.callback());
526 result = cb_open.GetResult(std::move(result));
527 if (result.net_error() != net::OK) {
528 command_marshal->ReturnFailure("Couldn't find key's entry.");
529 return nullptr;
530 }
531 Entry* cache_entry = result.ReleaseEntry();
532
533 const int kInitBufferSize = 8192;
534 scoped_refptr<net::GrowableIOBuffer> buffer =
535 base::MakeRefCounted<net::GrowableIOBuffer>();
536 buffer->SetCapacity(kInitBufferSize);
537 net::TestCompletionCallback cb;
538 while (true) {
539 int rv = cache_entry->ReadData(index, buffer->offset(), buffer.get(),
540 buffer->capacity() - buffer->offset(),
541 cb.callback());
542 rv = cb.GetResult(rv);
543 if (rv < 0) {
544 cache_entry->Close();
545 command_marshal->ReturnFailure("Stream read error.");
546 return nullptr;
547 }
548 buffer->set_offset(buffer->offset() + rv);
549 if (rv == 0)
550 break;
551 buffer->SetCapacity(buffer->offset() * 2);
552 }
553 cache_entry->Close();
554 return buffer;
555 }
556
557 // Prints a key's stream to stdout.
GetStreamForKey(CommandMarshal * command_marshal)558 void GetStreamForKey(CommandMarshal* command_marshal) {
559 std::string key = command_marshal->ReadString();
560 int index = command_marshal->ReadInt();
561 if (command_marshal->has_failed())
562 return;
563 scoped_refptr<net::GrowableIOBuffer> buffer(
564 GetStreamForKeyBuffer(command_marshal, key, index));
565 if (command_marshal->has_failed())
566 return;
567 if (index == kResponseInfoIndex) {
568 net::HttpResponseInfo response_info;
569 bool truncated_response_info = false;
570 if (!net::HttpCache::ParseResponseInfo(buffer->StartOfBuffer(),
571 buffer->offset(), &response_info,
572 &truncated_response_info)) {
573 // This can happen when reading data stored by content::CacheStorage.
574 std::cerr << "WARNING: Returning empty response info for key: " << key
575 << std::endl;
576 command_marshal->ReturnSuccess();
577 return command_marshal->ReturnString("");
578 }
579 if (truncated_response_info)
580 std::cerr << "WARNING: Truncated HTTP response." << std::endl;
581 command_marshal->ReturnSuccess();
582 command_marshal->ReturnString(
583 net::HttpUtil::ConvertHeadersBackToHTTPResponse(
584 response_info.headers->raw_headers()));
585 } else {
586 command_marshal->ReturnSuccess();
587 command_marshal->ReturnBuffer(buffer.get());
588 }
589 }
590
591 // Sets stdin as the key's raw response headers.
UpdateRawResponseHeaders(CommandMarshal * command_marshal)592 void UpdateRawResponseHeaders(CommandMarshal* command_marshal) {
593 std::string key = command_marshal->ReadString();
594 std::string raw_headers = command_marshal->ReadBufferedString();
595 if (command_marshal->has_failed())
596 return;
597 scoped_refptr<net::GrowableIOBuffer> buffer(
598 GetStreamForKeyBuffer(command_marshal, key, kResponseInfoIndex));
599 if (command_marshal->has_failed())
600 return;
601 net::HttpResponseInfo response_info;
602 bool truncated_response_info = false;
603 net::HttpCache::ParseResponseInfo(buffer->StartOfBuffer(), buffer->offset(),
604 &response_info, &truncated_response_info);
605 if (truncated_response_info)
606 std::cerr << "WARNING: Truncated HTTP response." << std::endl;
607
608 response_info.headers =
609 base::MakeRefCounted<net::HttpResponseHeaders>(raw_headers);
610 PersistResponseInfo(command_marshal, key, response_info);
611 }
612
613 // Sets a response header for a key.
SetHeader(CommandMarshal * command_marshal)614 void SetHeader(CommandMarshal* command_marshal) {
615 std::string key = command_marshal->ReadString();
616 std::string header_name = command_marshal->ReadString();
617 std::string header_value = command_marshal->ReadString();
618 if (command_marshal->has_failed())
619 return;
620
621 // Open the existing entry.
622 scoped_refptr<net::GrowableIOBuffer> buffer(
623 GetStreamForKeyBuffer(command_marshal, key, kResponseInfoIndex));
624 if (command_marshal->has_failed())
625 return;
626
627 // Read the entry into |response_info|.
628 net::HttpResponseInfo response_info;
629 bool truncated_response_info = false;
630 if (!net::HttpCache::ParseResponseInfo(buffer->StartOfBuffer(),
631 buffer->offset(), &response_info,
632 &truncated_response_info)) {
633 command_marshal->ReturnFailure("Couldn't read response info");
634 return;
635 }
636 if (truncated_response_info)
637 std::cerr << "WARNING: Truncated HTTP response." << std::endl;
638
639 // Update the header.
640 response_info.headers->SetHeader(header_name, header_value);
641
642 // Write the entry.
643 PersistResponseInfo(command_marshal, key, response_info);
644 }
645
646 // Deletes a specified key stream from the cache.
DeleteStreamForKey(CommandMarshal * command_marshal)647 void DeleteStreamForKey(CommandMarshal* command_marshal) {
648 std::string key = command_marshal->ReadString();
649 int index = command_marshal->ReadInt();
650 if (command_marshal->has_failed())
651 return;
652
653 TestEntryResultCompletionCallback cb_open;
654 EntryResult result = command_marshal->cache_backend()->OpenEntry(
655 key, net::HIGHEST, cb_open.callback());
656 result = cb_open.GetResult(std::move(result));
657 if (result.net_error() != net::OK)
658 return command_marshal->ReturnFailure("Couldn't find key's entry.");
659 Entry* cache_entry = result.ReleaseEntry();
660
661 net::TestCompletionCallback cb;
662 scoped_refptr<net::StringIOBuffer> buffer =
663 base::MakeRefCounted<net::StringIOBuffer>("");
664 int rv =
665 cache_entry->WriteData(index, 0, buffer.get(), 0, cb.callback(), true);
666 if (cb.GetResult(rv) != net::OK)
667 return command_marshal->ReturnFailure("Couldn't delete key stream.");
668 command_marshal->ReturnSuccess();
669 cache_entry->Close();
670 }
671
672 // Deletes a specified key from the cache.
DeleteKey(CommandMarshal * command_marshal)673 void DeleteKey(CommandMarshal* command_marshal) {
674 std::string key = command_marshal->ReadString();
675 if (command_marshal->has_failed())
676 return;
677 net::TestCompletionCallback cb;
678 int rv = command_marshal->cache_backend()->DoomEntry(key, net::HIGHEST,
679 cb.callback());
680 if (cb.GetResult(rv) != net::OK)
681 command_marshal->ReturnFailure("Couldn't delete key.");
682 else
683 command_marshal->ReturnSuccess();
684 }
685
686 // Executes all command from the |command_marshal|.
ExecuteCommands(CommandMarshal * command_marshal)687 bool ExecuteCommands(CommandMarshal* command_marshal) {
688 while (!command_marshal->has_failed()) {
689 std::string subcommand(command_marshal->ReadCommandName());
690 if (command_marshal->has_failed())
691 break;
692 if (subcommand == "stop") {
693 command_marshal->ReturnSuccess();
694 return true;
695 } else if (subcommand == "batch") {
696 StreamCommandMarshal stream_command_marshal(
697 command_marshal->cache_backend());
698 return ExecuteCommands(&stream_command_marshal);
699 } else if (subcommand == "delete_key") {
700 DeleteKey(command_marshal);
701 } else if (subcommand == "delete_stream") {
702 DeleteStreamForKey(command_marshal);
703 } else if (subcommand == "get_size") {
704 GetSize(command_marshal);
705 } else if (subcommand == "get_stream") {
706 GetStreamForKey(command_marshal);
707 } else if (subcommand == "list_keys") {
708 ListKeys(command_marshal);
709 } else if (subcommand == "update_raw_headers") {
710 UpdateRawResponseHeaders(command_marshal);
711 } else if (subcommand == "set_header") {
712 SetHeader(command_marshal);
713 } else if (subcommand == "list_dups") {
714 ListDups(command_marshal);
715 } else {
716 // The wrong subcommand is originated from the command line.
717 command_marshal->ReturnFailure("Unknown command.");
718 PrintHelp();
719 }
720 }
721 return false;
722 }
723
724 } // namespace
725
main(int argc,char * argv[])726 int main(int argc, char* argv[]) {
727 base::AtExitManager at_exit_manager;
728 base::SingleThreadTaskExecutor io_task_executor(base::MessagePumpType::IO);
729 base::CommandLine::Init(argc, argv);
730 const base::CommandLine& command_line =
731 *base::CommandLine::ForCurrentProcess();
732
733 base::CommandLine::StringVector args = command_line.GetArgs();
734 if (args.size() < 3U) {
735 PrintHelp();
736 return 1;
737 }
738
739 base::ThreadPoolInstance::CreateAndStartWithDefaultParams("cachetool");
740
741 base::FilePath cache_path(args[0]);
742 std::string cache_backend_type(args[1]);
743
744 net::BackendType backend_type;
745 if (cache_backend_type == "simple") {
746 backend_type = net::CACHE_BACKEND_SIMPLE;
747 } else if (cache_backend_type == "blockfile") {
748 backend_type = net::CACHE_BACKEND_BLOCKFILE;
749 } else {
750 std::cerr << "Unknown cache type." << std::endl;
751 PrintHelp();
752 return 1;
753 }
754
755 TestBackendResultCompletionCallback cb;
756 BackendResult result = disk_cache::CreateCacheBackend(
757 net::DISK_CACHE, backend_type, /*file_operations=*/nullptr, cache_path,
758 INT_MAX, disk_cache::ResetHandling::kNeverReset, /*net_log=*/nullptr,
759 cb.callback());
760 result = cb.GetResult(std::move(result));
761 if (result.net_error != net::OK) {
762 std::cerr << "Invalid cache." << std::endl;
763 return 1;
764 }
765 std::unique_ptr<Backend> cache_backend = std::move(result.backend);
766
767 ProgramArgumentCommandMarshal program_argument_marshal(
768 cache_backend.get(),
769 base::CommandLine::StringVector(args.begin() + 2, args.end()));
770 bool successful_commands = ExecuteCommands(&program_argument_marshal);
771
772 base::RunLoop().RunUntilIdle();
773 cache_backend = nullptr;
774 disk_cache::FlushCacheThreadForTesting();
775 base::RunLoop().RunUntilIdle();
776 return !successful_commands;
777 }
778