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