• 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 #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