• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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