Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / components / nacl / browser / pnacl_translation_cache.cc
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/nacl/browser/pnacl_translation_cache.h"
6
7 #include <string>
8
9 #include "base/callback.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/threading/thread_checker.h"
14 #include "components/nacl/common/pnacl_types.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
18 #include "net/disk_cache/disk_cache.h"
19
20 using base::IntToString;
21 using content::BrowserThread;
22
23 namespace {
24
25 void CloseDiskCacheEntry(disk_cache::Entry* entry) { entry->Close(); }
26
27 }  // namespace
28
29 namespace pnacl {
30 // This is in pnacl namespace instead of static so they can be used
31 // by the unit test.
32 const int kMaxMemCacheSize = 100 * 1024 * 1024;
33
34 //////////////////////////////////////////////////////////////////////
35 // Handle Reading/Writing to Cache.
36
37 // PnaclTranslationCacheEntry is a shim that provides storage for the
38 // 'key' and 'data' strings as the disk_cache is performing various async
39 // operations. It also tracks the open disk_cache::Entry
40 // and ensures that the entry is closed.
41 class PnaclTranslationCacheEntry
42     : public base::RefCounted<PnaclTranslationCacheEntry> {
43  public:
44   static PnaclTranslationCacheEntry* GetReadEntry(
45       base::WeakPtr<PnaclTranslationCache> cache,
46       const std::string& key,
47       const GetNexeCallback& callback);
48   static PnaclTranslationCacheEntry* GetWriteEntry(
49       base::WeakPtr<PnaclTranslationCache> cache,
50       const std::string& key,
51       net::DrainableIOBuffer* write_nexe,
52       const CompletionCallback& callback);
53
54   void Start();
55
56   // Writes:                                ---
57   //                                        v  |
58   // Start -> Open Existing --------------> Write ---> Close
59   //                          \              ^
60   //                           \             /
61   //                            --> Create --
62   // Reads:
63   // Start -> Open --------Read ----> Close
64   //                       |  ^
65   //                       |__|
66   enum CacheStep {
67     UNINITIALIZED,
68     OPEN_ENTRY,
69     CREATE_ENTRY,
70     TRANSFER_ENTRY,
71     CLOSE_ENTRY,
72     FINISHED
73   };
74
75  private:
76   friend class base::RefCounted<PnaclTranslationCacheEntry>;
77   PnaclTranslationCacheEntry(base::WeakPtr<PnaclTranslationCache> cache,
78                              const std::string& key,
79                              bool is_read);
80   ~PnaclTranslationCacheEntry();
81
82   // Try to open an existing entry in the backend
83   void OpenEntry();
84   // Create a new entry in the backend (for writes)
85   void CreateEntry();
86   // Write |len| bytes to the backend, starting at |offset|
87   void WriteEntry(int offset, int len);
88   // Read |len| bytes from the backend, starting at |offset|
89   void ReadEntry(int offset, int len);
90   // If there was an error, doom the entry. Then post a task to the IO
91   // thread to close (and delete) it.
92   void CloseEntry(int rv);
93   // Call the user callback, and signal to the cache to delete this.
94   void Finish(int rv);
95   // Used as the callback for all operations to the backend. Handle state
96   // transitions, track bytes transferred, and call the other helper methods.
97   void DispatchNext(int rv);
98
99   base::WeakPtr<PnaclTranslationCache> cache_;
100   std::string key_;
101   disk_cache::Entry* entry_;
102   CacheStep step_;
103   bool is_read_;
104   GetNexeCallback read_callback_;
105   CompletionCallback write_callback_;
106   scoped_refptr<net::DrainableIOBuffer> io_buf_;
107   base::ThreadChecker thread_checker_;
108   DISALLOW_COPY_AND_ASSIGN(PnaclTranslationCacheEntry);
109 };
110
111 // static
112 PnaclTranslationCacheEntry* PnaclTranslationCacheEntry::GetReadEntry(
113     base::WeakPtr<PnaclTranslationCache> cache,
114     const std::string& key,
115     const GetNexeCallback& callback) {
116   PnaclTranslationCacheEntry* entry(
117       new PnaclTranslationCacheEntry(cache, key, true));
118   entry->read_callback_ = callback;
119   return entry;
120 }
121
122 // static
123 PnaclTranslationCacheEntry* PnaclTranslationCacheEntry::GetWriteEntry(
124     base::WeakPtr<PnaclTranslationCache> cache,
125     const std::string& key,
126     net::DrainableIOBuffer* write_nexe,
127     const CompletionCallback& callback) {
128   PnaclTranslationCacheEntry* entry(
129       new PnaclTranslationCacheEntry(cache, key, false));
130   entry->io_buf_ = write_nexe;
131   entry->write_callback_ = callback;
132   return entry;
133 }
134
135 PnaclTranslationCacheEntry::PnaclTranslationCacheEntry(
136     base::WeakPtr<PnaclTranslationCache> cache,
137     const std::string& key,
138     bool is_read)
139     : cache_(cache),
140       key_(key),
141       entry_(NULL),
142       step_(UNINITIALIZED),
143       is_read_(is_read) {}
144
145 PnaclTranslationCacheEntry::~PnaclTranslationCacheEntry() {
146   // Ensure we have called the user's callback
147   if (step_ != FINISHED) {
148     if (!read_callback_.is_null()) {
149       BrowserThread::PostTask(
150           BrowserThread::IO,
151           FROM_HERE,
152           base::Bind(read_callback_,
153                      net::ERR_ABORTED,
154                      scoped_refptr<net::DrainableIOBuffer>()));
155     }
156     if (!write_callback_.is_null()) {
157       BrowserThread::PostTask(BrowserThread::IO,
158                               FROM_HERE,
159                               base::Bind(write_callback_, net::ERR_ABORTED));
160     }
161   }
162 }
163
164 void PnaclTranslationCacheEntry::Start() {
165   DCHECK(thread_checker_.CalledOnValidThread());
166   step_ = OPEN_ENTRY;
167   OpenEntry();
168 }
169
170 // OpenEntry, CreateEntry, WriteEntry, ReadEntry and CloseEntry are only called
171 // from DispatchNext, so they know that cache_ is still valid.
172 void PnaclTranslationCacheEntry::OpenEntry() {
173   int rv = cache_->backend()->OpenEntry(
174       key_,
175       &entry_,
176       base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this));
177   if (rv != net::ERR_IO_PENDING)
178     DispatchNext(rv);
179 }
180
181 void PnaclTranslationCacheEntry::CreateEntry() {
182   int rv = cache_->backend()->CreateEntry(
183       key_,
184       &entry_,
185       base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this));
186   if (rv != net::ERR_IO_PENDING)
187     DispatchNext(rv);
188 }
189
190 void PnaclTranslationCacheEntry::WriteEntry(int offset, int len) {
191   DCHECK(io_buf_->BytesRemaining() == len);
192   int rv = entry_->WriteData(
193       1,
194       offset,
195       io_buf_.get(),
196       len,
197       base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this),
198       false);
199   if (rv != net::ERR_IO_PENDING)
200     DispatchNext(rv);
201 }
202
203 void PnaclTranslationCacheEntry::ReadEntry(int offset, int len) {
204   int rv = entry_->ReadData(
205       1,
206       offset,
207       io_buf_.get(),
208       len,
209       base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this));
210   if (rv != net::ERR_IO_PENDING)
211     DispatchNext(rv);
212 }
213
214 void PnaclTranslationCacheEntry::CloseEntry(int rv) {
215   DCHECK(entry_);
216   if (rv < 0) {
217     LOG(ERROR) << "Failed to close entry: " << net::ErrorToString(rv);
218     entry_->Doom();
219   }
220   BrowserThread::PostTask(
221       BrowserThread::IO, FROM_HERE, base::Bind(&CloseDiskCacheEntry, entry_));
222   Finish(rv);
223 }
224
225 void PnaclTranslationCacheEntry::Finish(int rv) {
226   step_ = FINISHED;
227   if (is_read_) {
228     if (!read_callback_.is_null()) {
229       BrowserThread::PostTask(BrowserThread::IO,
230                               FROM_HERE,
231                               base::Bind(read_callback_, rv, io_buf_));
232     }
233   } else {
234     if (!write_callback_.is_null()) {
235       BrowserThread::PostTask(
236           BrowserThread::IO, FROM_HERE, base::Bind(write_callback_, rv));
237     }
238   }
239   cache_->OpComplete(this);
240 }
241
242 void PnaclTranslationCacheEntry::DispatchNext(int rv) {
243   DCHECK(thread_checker_.CalledOnValidThread());
244   if (!cache_)
245     return;
246
247   switch (step_) {
248     case UNINITIALIZED:
249     case FINISHED:
250       LOG(ERROR) << "DispatchNext called uninitialized";
251       break;
252
253     case OPEN_ENTRY:
254       if (rv == net::OK) {
255         step_ = TRANSFER_ENTRY;
256         if (is_read_) {
257           int bytes_to_transfer = entry_->GetDataSize(1);
258           io_buf_ = new net::DrainableIOBuffer(
259               new net::IOBuffer(bytes_to_transfer), bytes_to_transfer);
260           ReadEntry(0, bytes_to_transfer);
261         } else {
262           WriteEntry(0, io_buf_->size());
263         }
264       } else {
265         if (rv != net::ERR_FAILED) {
266           // ERROR_FAILED is what we expect if the entry doesn't exist.
267           LOG(ERROR) << "OpenEntry failed: " << net::ErrorToString(rv);
268         }
269         if (is_read_) {
270           // Just a cache miss, not necessarily an error.
271           entry_ = NULL;
272           Finish(rv);
273         } else {
274           step_ = CREATE_ENTRY;
275           CreateEntry();
276         }
277       }
278       break;
279
280     case CREATE_ENTRY:
281       if (rv == net::OK) {
282         step_ = TRANSFER_ENTRY;
283         WriteEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining());
284       } else {
285         LOG(ERROR) << "Failed to Create Entry: " << net::ErrorToString(rv);
286         Finish(rv);
287       }
288       break;
289
290     case TRANSFER_ENTRY:
291       if (rv < 0) {
292         // We do not call DispatchNext directly if WriteEntry/ReadEntry returns
293         // ERR_IO_PENDING, and the callback should not return that value either.
294         LOG(ERROR) << "Failed to complete write to entry: "
295                    << net::ErrorToString(rv);
296         step_ = CLOSE_ENTRY;
297         CloseEntry(rv);
298         break;
299       } else if (rv > 0) {
300         io_buf_->DidConsume(rv);
301         if (io_buf_->BytesRemaining() > 0) {
302           is_read_
303               ? ReadEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining())
304               : WriteEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining());
305           break;
306         }
307       }
308       // rv == 0 or we fell through (i.e. we have transferred all the bytes)
309       step_ = CLOSE_ENTRY;
310       DCHECK(io_buf_->BytesConsumed() == io_buf_->size());
311       if (is_read_)
312         io_buf_->SetOffset(0);
313       CloseEntry(0);
314       break;
315
316     case CLOSE_ENTRY:
317       step_ = UNINITIALIZED;
318       break;
319   }
320 }
321
322 //////////////////////////////////////////////////////////////////////
323 void PnaclTranslationCache::OpComplete(PnaclTranslationCacheEntry* entry) {
324   open_entries_.erase(entry);
325 }
326
327 //////////////////////////////////////////////////////////////////////
328 // Construction and cache backend initialization
329 PnaclTranslationCache::PnaclTranslationCache() : in_memory_(false) {}
330
331 PnaclTranslationCache::~PnaclTranslationCache() {}
332
333 int PnaclTranslationCache::Init(net::CacheType cache_type,
334                                 const base::FilePath& cache_dir,
335                                 int cache_size,
336                                 const CompletionCallback& callback) {
337   int rv = disk_cache::CreateCacheBackend(
338       cache_type,
339       net::CACHE_BACKEND_DEFAULT,
340       cache_dir,
341       cache_size,
342       true /* force_initialize */,
343       BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(),
344       NULL, /* dummy net log */
345       &disk_cache_,
346       base::Bind(&PnaclTranslationCache::OnCreateBackendComplete, AsWeakPtr()));
347   if (rv == net::ERR_IO_PENDING) {
348     init_callback_ = callback;
349   }
350   return rv;
351 }
352
353 void PnaclTranslationCache::OnCreateBackendComplete(int rv) {
354   if (rv < 0) {
355     LOG(ERROR) << "Backend init failed:" << net::ErrorToString(rv);
356   }
357   // Invoke our client's callback function.
358   if (!init_callback_.is_null()) {
359     BrowserThread::PostTask(
360         BrowserThread::IO, FROM_HERE, base::Bind(init_callback_, rv));
361   }
362 }
363
364 //////////////////////////////////////////////////////////////////////
365 // High-level API
366
367 void PnaclTranslationCache::StoreNexe(const std::string& key,
368                                       net::DrainableIOBuffer* nexe_data,
369                                       const CompletionCallback& callback) {
370   PnaclTranslationCacheEntry* entry = PnaclTranslationCacheEntry::GetWriteEntry(
371       AsWeakPtr(), key, nexe_data, callback);
372   open_entries_[entry] = entry;
373   entry->Start();
374 }
375
376 void PnaclTranslationCache::GetNexe(const std::string& key,
377                                     const GetNexeCallback& callback) {
378   PnaclTranslationCacheEntry* entry =
379       PnaclTranslationCacheEntry::GetReadEntry(AsWeakPtr(), key, callback);
380   open_entries_[entry] = entry;
381   entry->Start();
382 }
383
384 int PnaclTranslationCache::InitOnDisk(const base::FilePath& cache_directory,
385                                       const CompletionCallback& callback) {
386   in_memory_ = false;
387   return Init(net::PNACL_CACHE, cache_directory, 0 /* auto size */, callback);
388 }
389
390 int PnaclTranslationCache::InitInMemory(const CompletionCallback& callback) {
391   in_memory_ = true;
392   return Init(net::MEMORY_CACHE, base::FilePath(), kMaxMemCacheSize, callback);
393 }
394
395 int PnaclTranslationCache::Size() {
396   if (!disk_cache_)
397     return -1;
398   return disk_cache_->GetEntryCount();
399 }
400
401 // Beware that any changes to this function or to PnaclCacheInfo will
402 // effectively invalidate existing translation cache entries.
403
404 // static
405 std::string PnaclTranslationCache::GetKey(const nacl::PnaclCacheInfo& info) {
406   if (!info.pexe_url.is_valid() || info.abi_version < 0 || info.opt_level < 0 ||
407       info.extra_flags.size() > 512)
408     return std::string();
409   std::string retval("ABI:");
410   retval += IntToString(info.abi_version) + ";" + "opt:" +
411             IntToString(info.opt_level) + ";" + "URL:";
412   // Filter the username, password, and ref components from the URL
413   GURL::Replacements replacements;
414   replacements.ClearUsername();
415   replacements.ClearPassword();
416   replacements.ClearRef();
417   GURL key_url(info.pexe_url.ReplaceComponents(replacements));
418   retval += key_url.spec() + ";";
419   // You would think that there is already code to format base::Time values
420   // somewhere, but I haven't found it yet. In any case, doing it ourselves
421   // here means we can keep the format stable.
422   base::Time::Exploded exploded;
423   info.last_modified.UTCExplode(&exploded);
424   if (info.last_modified.is_null() || !exploded.HasValidValues()) {
425     memset(&exploded, 0, sizeof(exploded));
426   }
427   retval += "modified:" + IntToString(exploded.year) + ":" +
428             IntToString(exploded.month) + ":" +
429             IntToString(exploded.day_of_month) + ":" +
430             IntToString(exploded.hour) + ":" + IntToString(exploded.minute) +
431             ":" + IntToString(exploded.second) + ":" +
432             IntToString(exploded.millisecond) + ":UTC;";
433   retval += "etag:" + info.etag + ";";
434   retval += "sandbox:" + info.sandbox_isa + ";";
435   retval += "extra_flags:" + info.extra_flags + ";";
436   return retval;
437 }
438
439 int PnaclTranslationCache::DoomEntriesBetween(
440     base::Time initial,
441     base::Time end,
442     const CompletionCallback& callback) {
443   return disk_cache_->DoomEntriesBetween(initial, end, callback);
444 }
445
446 }  // namespace pnacl