Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / net / http / http_cache.cc
index 30742c8..a167e32 100644 (file)
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/callback.h"
-#include "base/file_util.h"
+#include "base/files/file_util.h"
 #include "base/format_macros.h"
 #include "base/location.h"
 #include "base/memory/ref_counted.h"
 #include "base/message_loop/message_loop.h"
 #include "base/metrics/field_trial.h"
+#include "base/metrics/histogram.h"
 #include "base/pickle.h"
+#include "base/profiler/scoped_tracker.h"
 #include "base/stl_util.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "base/threading/worker_pool.h"
+#include "base/time/time.h"
 #include "net/base/cache_type.h"
 #include "net/base/io_buffer.h"
 #include "net/base/load_flags.h"
 #include "net/base/net_errors.h"
+#include "net/base/network_delegate.h"
 #include "net/base/upload_data_stream.h"
 #include "net/disk_cache/disk_cache.h"
 #include "net/http/disk_based_cert_cache.h"
@@ -60,11 +64,12 @@ void DeletePath(base::FilePath path) {
 
 namespace net {
 
-HttpCache::DefaultBackend::DefaultBackend(CacheType type,
-                                          BackendType backend_type,
-                                          const base::FilePath& path,
-                                          int max_bytes,
-                                          base::MessageLoopProxy* thread)
+HttpCache::DefaultBackend::DefaultBackend(
+    CacheType type,
+    BackendType backend_type,
+    const base::FilePath& path,
+    int max_bytes,
+    const scoped_refptr<base::SingleThreadTaskRunner>& thread)
     : type_(type),
       backend_type_(backend_type),
       path_(path),
@@ -89,7 +94,7 @@ int HttpCache::DefaultBackend::CreateBackend(
                                         path_,
                                         max_bytes_,
                                         true,
-                                        thread_.get(),
+                                        thread_,
                                         net_log,
                                         backend,
                                         callback);
@@ -277,12 +282,11 @@ void HttpCache::MetadataWriter::OnIOComplete(int result) {
 
 class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory {
  public:
-  QuicServerInfoFactoryAdaptor(HttpCache* http_cache)
+  explicit QuicServerInfoFactoryAdaptor(HttpCache* http_cache)
       : http_cache_(http_cache) {
   }
 
-  virtual QuicServerInfo* GetForServer(
-      const QuicServerId& server_id) OVERRIDE {
+  QuicServerInfo* GetForServer(const QuicServerId& server_id) override {
     return new DiskCacheBasedQuicServerInfo(server_id, http_cache_);
   }
 
@@ -291,12 +295,166 @@ class HttpCache::QuicServerInfoFactoryAdaptor : public QuicServerInfoFactory {
 };
 
 //-----------------------------------------------------------------------------
+
+class HttpCache::AsyncValidation {
+ public:
+  AsyncValidation(const HttpRequestInfo& original_request, HttpCache* cache)
+      : request_(original_request), cache_(cache) {}
+  ~AsyncValidation() {}
+
+  void Start(const BoundNetLog& net_log,
+             scoped_ptr<Transaction> transaction,
+             NetworkDelegate* network_delegate);
+
+ private:
+  void OnStarted(int result);
+  void DoRead();
+  void OnRead(int result);
+
+  // Terminate this request with net error code |result|. Logs the transaction
+  // result and asks HttpCache to delete this object.
+  // If there was a client or server certificate error, it cannot be recovered
+  // asynchronously, so we need to prevent future attempts to asynchronously
+  // fetch the resource. In this case, the cache entry is doomed.
+  void Terminate(int result);
+
+  HttpRequestInfo request_;
+  scoped_refptr<IOBuffer> buf_;
+  CompletionCallback read_callback_;
+  scoped_ptr<Transaction> transaction_;
+  base::Time start_time_;
+
+  // The HttpCache object owns this object. This object is always deleted before
+  // the pointer to the cache becomes invalid.
+  HttpCache* cache_;
+
+  DISALLOW_COPY_AND_ASSIGN(AsyncValidation);
+};
+
+void HttpCache::AsyncValidation::Start(const BoundNetLog& net_log,
+                                       scoped_ptr<Transaction> transaction,
+                                       NetworkDelegate* network_delegate) {
+  transaction_ = transaction.Pass();
+  if (network_delegate) {
+    // This code is necessary to enable async transactions to pass over the
+    // data-reduction proxy. This is a violation of the "once-and-only-once"
+    // principle, since it copies code from URLRequestHttpJob. We cannot use the
+    // original callback passed to HttpCache::Transaction by URLRequestHttpJob
+    // as it will only be valid as long as the URLRequestHttpJob object is
+    // alive, and that object will be deleted as soon as the synchronous request
+    // completes.
+    //
+    // This code is also an encapsulation violation. We are exploiting the fact
+    // that the |request| parameter to NotifyBeforeSendProxyHeaders() is never
+    // actually used for anything, and so can be NULL.
+    //
+    // TODO(ricea): Do this better.
+    transaction_->SetBeforeProxyHeadersSentCallback(
+        base::Bind(&NetworkDelegate::NotifyBeforeSendProxyHeaders,
+                   base::Unretained(network_delegate),
+                   static_cast<URLRequest*>(NULL)));
+    // The above use of base::Unretained is safe because the NetworkDelegate has
+    // to live at least as long as the HttpNetworkSession which has to live as
+    // least as long as the HttpNetworkLayer which has to live at least as long
+    // this HttpCache object.
+  }
+
+  DCHECK_EQ(0, request_.load_flags & LOAD_ASYNC_REVALIDATION);
+  request_.load_flags |= LOAD_ASYNC_REVALIDATION;
+  start_time_ = base::Time::Now();
+  // This use of base::Unretained is safe because |transaction_| is owned by
+  // this object.
+  read_callback_ = base::Bind(&AsyncValidation::OnRead, base::Unretained(this));
+  // This use of base::Unretained is safe as above.
+  int rv = transaction_->Start(
+      &request_,
+      base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)),
+      net_log);
+
+  if (rv == ERR_IO_PENDING)
+    return;
+
+  OnStarted(rv);
+}
+
+void HttpCache::AsyncValidation::OnStarted(int result) {
+  if (result != OK) {
+    DVLOG(1) << "Asynchronous transaction start failed for " << request_.url;
+    Terminate(result);
+    return;
+  }
+
+  while (transaction_->IsReadyToRestartForAuth()) {
+    // This code is based on URLRequestHttpJob::RestartTransactionWithAuth,
+    // however when we do this here cookies on the response will not be
+    // stored. Fortunately only a tiny number of sites set cookies on 401
+    // responses, and none of them use stale-while-revalidate.
+    result = transaction_->RestartWithAuth(
+        AuthCredentials(),
+        base::Bind(&AsyncValidation::OnStarted, base::Unretained(this)));
+    if (result == ERR_IO_PENDING)
+      return;
+    if (result != OK) {
+      DVLOG(1) << "Synchronous transaction restart with auth failed for "
+               << request_.url;
+      Terminate(result);
+      return;
+    }
+  }
+
+  DoRead();
+}
+
+void HttpCache::AsyncValidation::DoRead() {
+  const size_t kBufSize = 4096;
+  if (!buf_.get())
+    buf_ = new IOBuffer(kBufSize);
+
+  int rv = 0;
+  do {
+    rv = transaction_->Read(buf_.get(), kBufSize, read_callback_);
+  } while (rv > 0);
+
+  if (rv == ERR_IO_PENDING)
+    return;
+
+  OnRead(rv);
+}
+
+void HttpCache::AsyncValidation::OnRead(int result) {
+  if (result > 0) {
+    DoRead();
+    return;
+  }
+  Terminate(result);
+}
+
+void HttpCache::AsyncValidation::Terminate(int result) {
+  if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED || IsCertificateError(result)) {
+    // We should not attempt to access this resource asynchronously again until
+    // the certificate problem has been resolved.
+    // TODO(ricea): For ERR_SSL_CLIENT_AUTH_CERT_NEEDED, mark the entry as
+    // requiring synchronous revalidation rather than just deleting it. Other
+    // certificate errors cause the resource to be considered uncacheable
+    // anyway.
+    cache_->DoomEntry(transaction_->key(), transaction_.get());
+  }
+  base::TimeDelta duration = base::Time::Now() - start_time_;
+  UMA_HISTOGRAM_TIMES("HttpCache.AsyncValidationDuration", duration);
+  transaction_->net_log().EndEventWithNetErrorCode(
+      NetLog::TYPE_ASYNC_REVALIDATION, result);
+  cache_->DeleteAsyncValidation(cache_->GenerateCacheKey(&request_));
+  // |this| is deleted.
+}
+
+//-----------------------------------------------------------------------------
 HttpCache::HttpCache(const net::HttpNetworkSession::Params& params,
                      BackendFactory* backend_factory)
     : net_log_(params.net_log),
       backend_factory_(backend_factory),
       building_backend_(false),
       bypass_lock_for_test_(false),
+      use_stale_while_revalidate_(params.use_stale_while_revalidate),
       mode_(NORMAL),
       network_layer_(new HttpNetworkLayer(new HttpNetworkSession(params))),
       weak_factory_(this) {
@@ -312,6 +470,7 @@ HttpCache::HttpCache(HttpNetworkSession* session,
       backend_factory_(backend_factory),
       building_backend_(false),
       bypass_lock_for_test_(false),
+      use_stale_while_revalidate_(session->params().use_stale_while_revalidate),
       mode_(NORMAL),
       network_layer_(new HttpNetworkLayer(session)),
       weak_factory_(this) {
@@ -324,10 +483,14 @@ HttpCache::HttpCache(HttpTransactionFactory* network_layer,
       backend_factory_(backend_factory),
       building_backend_(false),
       bypass_lock_for_test_(false),
+      use_stale_while_revalidate_(false),
       mode_(NORMAL),
       network_layer_(network_layer),
       weak_factory_(this) {
   SetupQuicServerInfoFactory(network_layer_->GetSession());
+  HttpNetworkSession* session = network_layer_->GetSession();
+  if (session)
+    use_stale_while_revalidate_ = session->params().use_stale_while_revalidate;
 }
 
 HttpCache::~HttpCache() {
@@ -349,6 +512,7 @@ HttpCache::~HttpCache() {
   }
 
   STLDeleteElements(&doomed_entries_);
+  STLDeleteValues(&async_validations_);
 
   // Before deleting pending_ops_, we have to make sure that the disk cache is
   // done with said operations, or it will attempt to use deleted data.
@@ -612,7 +776,8 @@ int HttpCache::DoomEntry(const std::string& key, Transaction* trans) {
   entry->disk_entry->Doom();
   entry->doomed = true;
 
-  DCHECK(entry->writer || !entry->readers.empty());
+  DCHECK(entry->writer || !entry->readers.empty() ||
+         entry->will_process_pending_queue);
   return OK;
 }
 
@@ -1036,6 +1201,43 @@ void HttpCache::ProcessPendingQueue(ActiveEntry* entry) {
       base::Bind(&HttpCache::OnProcessPendingQueue, GetWeakPtr(), entry));
 }
 
+void HttpCache::PerformAsyncValidation(const HttpRequestInfo& original_request,
+                                       const BoundNetLog& net_log) {
+  DCHECK(use_stale_while_revalidate_);
+  std::string key = GenerateCacheKey(&original_request);
+  AsyncValidation* async_validation =
+      new AsyncValidation(original_request, this);
+  typedef AsyncValidationMap::value_type AsyncValidationKeyValue;
+  bool insert_ok =
+      async_validations_.insert(AsyncValidationKeyValue(key, async_validation))
+          .second;
+  if (!insert_ok) {
+    DVLOG(1) << "Harmless race condition detected on URL "
+             << original_request.url << "; discarding redundant revalidation.";
+    delete async_validation;
+    return;
+  }
+  HttpNetworkSession* network_session = GetSession();
+  NetworkDelegate* network_delegate = NULL;
+  if (network_session)
+    network_delegate = network_session->network_delegate();
+  scoped_ptr<HttpTransaction> transaction;
+  CreateTransaction(IDLE, &transaction);
+  scoped_ptr<Transaction> downcast_transaction(
+      static_cast<Transaction*>(transaction.release()));
+  async_validation->Start(
+      net_log, downcast_transaction.Pass(), network_delegate);
+  // |async_validation| may have been deleted here.
+}
+
+void HttpCache::DeleteAsyncValidation(const std::string& url) {
+  AsyncValidationMap::iterator it = async_validations_.find(url);
+  CHECK(it != async_validations_.end());  // security-critical invariant
+  AsyncValidation* async_validation = it->second;
+  async_validations_.erase(it);
+  delete async_validation;
+}
+
 void HttpCache::OnProcessPendingQueue(ActiveEntry* entry) {
   entry->will_process_pending_queue = false;
   DCHECK(!entry->writer);
@@ -1153,6 +1355,11 @@ void HttpCache::OnIOComplete(int result, PendingOp* pending_op) {
 void HttpCache::OnPendingOpComplete(const base::WeakPtr<HttpCache>& cache,
                                     PendingOp* pending_op,
                                     int rv) {
+  // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
+  tracked_objects::ScopedTracker tracking_profile(
+      FROM_HERE_WITH_EXPLICIT_FUNCTION(
+          "422516 HttpCache::OnPendingOpComplete"));
+
   if (cache.get()) {
     cache->OnIOComplete(rv, pending_op);
   } else {