| Index: shell/application_manager/network_fetcher.cc
|
| diff --git a/shell/application_manager/network_fetcher.cc b/shell/application_manager/network_fetcher.cc
|
| index 31caa194ce9193fe80fe0b453b50c7061b458b3f..039cbea3958a2b296dd967a14dc7aded73c513df 100644
|
| --- a/shell/application_manager/network_fetcher.cc
|
| +++ b/shell/application_manager/network_fetcher.cc
|
| @@ -32,8 +32,96 @@ char kArchitecture[] = "android-arm";
|
| #else
|
| #error "Unsupported."
|
| #endif
|
| +
|
| +// The delay to wait before trying to update an application after having served
|
| +// it from the cache.
|
| +const uint32_t kUpdateApplicationDelayInSeconds = 60;
|
| +
|
| +base::FilePath ToFilePath(const mojo::Array<uint8_t>& array) {
|
| + return base::FilePath(
|
| + std::string(reinterpret_cast<const char*>(&array.front()), array.size()));
|
| +}
|
| +
|
| +void IgnoreResult(bool result) {}
|
| +
|
| +mojo::URLRequestPtr GetRequest(const GURL& url, bool disable_cache) {
|
| + mojo::URLRequestPtr request(mojo::URLRequest::New());
|
| + request->url = mojo::String::From(url);
|
| + request->auto_follow_redirects = false;
|
| + if (disable_cache)
|
| + request->cache_mode = mojo::URLRequest::CACHE_MODE_BYPASS_CACHE;
|
| + auto architecture_header = mojo::HttpHeader::New();
|
| + architecture_header->name = "X-Architecture";
|
| + architecture_header->value = kArchitecture;
|
| + mojo::Array<mojo::HttpHeaderPtr> headers;
|
| + headers.push_back(architecture_header.Pass());
|
| + request->headers = headers.Pass();
|
| +
|
| + return request.Pass();
|
| +}
|
| +
|
| +// This class is self owned and will delete itself after having tried to update
|
| +// the application cache.
|
| +class ApplicationUpdater : public base::MessageLoop::DestructionObserver {
|
| + public:
|
| + ApplicationUpdater(const GURL& url,
|
| + const base::TimeDelta& update_delay,
|
| + mojo::URLResponseDiskCache* url_response_disk_cache,
|
| + mojo::NetworkService* network_service);
|
| + ~ApplicationUpdater() override;
|
| +
|
| + private:
|
| + // DestructionObserver
|
| + void WillDestroyCurrentMessageLoop() override;
|
| +
|
| + void UpdateApplication();
|
| + void OnLoadComplete(mojo::URLResponsePtr response);
|
| +
|
| + GURL url_;
|
| + base::TimeDelta update_delay_;
|
| + mojo::URLResponseDiskCache* url_response_disk_cache_;
|
| + mojo::NetworkService* network_service_;
|
| + mojo::URLLoaderPtr url_loader_;
|
| };
|
|
|
| +ApplicationUpdater::ApplicationUpdater(
|
| + const GURL& url,
|
| + const base::TimeDelta& update_delay,
|
| + mojo::URLResponseDiskCache* url_response_disk_cache,
|
| + mojo::NetworkService* network_service)
|
| + : url_(url),
|
| + update_delay_(update_delay),
|
| + url_response_disk_cache_(url_response_disk_cache),
|
| + network_service_(network_service) {
|
| + base::MessageLoop::current()->AddDestructionObserver(this);
|
| + base::MessageLoop::current()->PostDelayedTask(
|
| + FROM_HERE, base::Bind(&ApplicationUpdater::UpdateApplication,
|
| + base::Unretained(this)),
|
| + update_delay_);
|
| +}
|
| +
|
| +ApplicationUpdater::~ApplicationUpdater() {
|
| + base::MessageLoop::current()->RemoveDestructionObserver(this);
|
| +}
|
| +
|
| +void ApplicationUpdater::WillDestroyCurrentMessageLoop() {
|
| + delete this;
|
| +}
|
| +
|
| +void ApplicationUpdater::UpdateApplication() {
|
| + network_service_->CreateURLLoader(GetProxy(&url_loader_));
|
| + url_loader_->Start(
|
| + GetRequest(url_, false),
|
| + base::Bind(&ApplicationUpdater::OnLoadComplete, base::Unretained(this)));
|
| +}
|
| +
|
| +void ApplicationUpdater::OnLoadComplete(mojo::URLResponsePtr response) {
|
| + url_response_disk_cache_->Update(response.Pass());
|
| + delete this;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| NetworkFetcher::NetworkFetcher(
|
| bool disable_cache,
|
| const GURL& url,
|
| @@ -46,7 +134,11 @@ NetworkFetcher::NetworkFetcher(
|
| url_response_disk_cache_(url_response_disk_cache),
|
| network_service_(network_service),
|
| weak_ptr_factory_(this) {
|
| - StartNetworkRequest(FROM_NETWORK);
|
| + if (CanLoadDirectlyFromCache()) {
|
| + LoadFromCache(true);
|
| + } else {
|
| + StartNetworkRequest();
|
| + }
|
| }
|
|
|
| NetworkFetcher::~NetworkFetcher() {
|
| @@ -69,61 +161,25 @@ GURL NetworkFetcher::GetRedirectURL() const {
|
| mojo::URLResponsePtr NetworkFetcher::AsURLResponse(
|
| base::TaskRunner* task_runner,
|
| uint32_t skip) {
|
| - if (skip != 0) {
|
| - MojoResult result = ReadDataRaw(
|
| - response_->body.get(), nullptr, &skip,
|
| - MOJO_READ_DATA_FLAG_ALL_OR_NONE | MOJO_READ_DATA_FLAG_DISCARD);
|
| - DCHECK_EQ(result, MOJO_RESULT_OK);
|
| - }
|
| + DCHECK(response_);
|
| + DCHECK(!path_.empty());
|
| + mojo::DataPipe data_pipe;
|
| + response_->body = data_pipe.consumer_handle.Pass();
|
| + mojo::common::CopyFromFile(path_, data_pipe.producer_handle.Pass(), skip,
|
| + task_runner, base::Bind(&IgnoreResult));
|
| return response_.Pass();
|
| }
|
|
|
| -void NetworkFetcher::RecordCacheToURLMapping(const base::FilePath& path,
|
| - const GURL& url) {
|
| - // This is used to extract symbols on android.
|
| - // TODO(eseidel): All users of this log should move to using the map file.
|
| - LOG(INFO) << "Caching mojo app " << url << " at " << path.value();
|
| -
|
| - base::FilePath temp_dir;
|
| - base::GetTempDir(&temp_dir);
|
| - base::ProcessId pid = base::Process::Current().Pid();
|
| - std::string map_name = base::StringPrintf("mojo_shell.%d.maps", pid);
|
| - base::FilePath map_path = temp_dir.Append(map_name);
|
| -
|
| - // TODO(eseidel): Paths or URLs with spaces will need quoting.
|
| - std::string map_entry =
|
| - base::StringPrintf("%s %s\n", path.value().c_str(), url.spec().c_str());
|
| - // TODO(eseidel): AppendToFile is missing O_CREAT, crbug.com/450696
|
| - if (!PathExists(map_path))
|
| - base::WriteFile(map_path, map_entry.data(), map_entry.length());
|
| - else
|
| - base::AppendToFile(map_path, map_entry.data(), map_entry.length());
|
| -}
|
| -
|
| -void NetworkFetcher::OnFileRetrievedFromCache(
|
| - base::Callback<void(const base::FilePath&, bool)> callback,
|
| - mojo::Array<uint8_t> path_as_array,
|
| - mojo::Array<uint8_t> cache_dir) {
|
| - bool success = !path_as_array.is_null();
|
| - if (success) {
|
| - path_ = base::FilePath(std::string(
|
| - reinterpret_cast<char*>(&path_as_array.front()), path_as_array.size()));
|
| - RecordCacheToURLMapping(path_, url_);
|
| - }
|
| -
|
| - base::MessageLoop::current()->PostTask(FROM_HERE,
|
| - base::Bind(callback, path_, success));
|
| -}
|
| -
|
| void NetworkFetcher::AsPath(
|
| base::TaskRunner* task_runner,
|
| base::Callback<void(const base::FilePath&, bool)> callback) {
|
| // This should only called once, when we have a response.
|
| DCHECK(response_.get());
|
|
|
| - url_response_disk_cache_->GetFile(
|
| - response_.Pass(), base::Bind(&NetworkFetcher::OnFileRetrievedFromCache,
|
| - weak_ptr_factory_.GetWeakPtr(), callback));
|
| + base::MessageLoop::current()->PostTask(
|
| + FROM_HERE, base::Bind(callback, path_, base::PathExists(path_)));
|
| + response_.reset();
|
| + return;
|
| }
|
|
|
| std::string NetworkFetcher::MimeType() {
|
| @@ -131,51 +187,67 @@ std::string NetworkFetcher::MimeType() {
|
| }
|
|
|
| bool NetworkFetcher::HasMojoMagic() {
|
| - std::string magic;
|
| - return BlockingPeekNBytes(response_->body.get(), &magic, strlen(kMojoMagic),
|
| - kPeekTimeout) &&
|
| - magic == kMojoMagic;
|
| + return Fetcher::HasMojoMagic(path_);
|
| }
|
|
|
| bool NetworkFetcher::PeekFirstLine(std::string* line) {
|
| - return BlockingPeekLine(response_->body.get(), line, kMaxShebangLength,
|
| - kPeekTimeout);
|
| + return Fetcher::PeekFirstLine(path_, line);
|
| }
|
|
|
| -void NetworkFetcher::StartNetworkRequest(RequestType request_type) {
|
| - TRACE_EVENT_ASYNC_BEGIN1("mojo_shell", "NetworkFetcher::NetworkRequest", this,
|
| - "url", url_.spec());
|
| - mojo::URLRequestPtr request(mojo::URLRequest::New());
|
| - request->url = mojo::String::From(url_);
|
| - request->auto_follow_redirects = false;
|
| +bool NetworkFetcher::CanLoadDirectlyFromCache() {
|
| if (disable_cache_)
|
| - request->cache_mode = mojo::URLRequest::CACHE_MODE_BYPASS_CACHE;
|
| - auto header = mojo::HttpHeader::New();
|
| - header->name = "X-Architecture";
|
| - header->value = kArchitecture;
|
| - mojo::Array<mojo::HttpHeaderPtr> headers;
|
| - headers.push_back(header.Pass());
|
| - request->headers = headers.Pass();
|
| + return false;
|
| +
|
| + const std::string& host = url_.host();
|
| + return !(host == "localhost" || host == "127.0.0.1" || host == "[::1]");
|
| +}
|
| +
|
| +void NetworkFetcher::LoadFromCache(bool schedule_update) {
|
| + url_response_disk_cache_->Get(
|
| + mojo::String::From(url_),
|
| + base::Bind(&NetworkFetcher::OnCachedResponseReceived,
|
| + base::Unretained(this), schedule_update));
|
| +}
|
|
|
| +void NetworkFetcher::OnCachedResponseReceived(
|
| + bool schedule_update,
|
| + mojo::URLResponsePtr response,
|
| + mojo::Array<uint8_t> path_as_array,
|
| + mojo::Array<uint8_t> cache_dir) {
|
| + if (!response) {
|
| + // Not in cache, loading from net.
|
| + StartNetworkRequest();
|
| + return;
|
| + }
|
| + if (schedule_update) {
|
| + // The response has been found in the cache. Plan updating the application.
|
| + new ApplicationUpdater(
|
| + url_, base::TimeDelta::FromSeconds(kUpdateApplicationDelayInSeconds),
|
| + url_response_disk_cache_, network_service_);
|
| + }
|
| + response_ = response.Pass();
|
| + path_ = ToFilePath(path_as_array);
|
| + RecordCacheToURLMapping(path_, url_);
|
| + loader_callback_.Run(make_scoped_ptr(this));
|
| +}
|
| +
|
| +void NetworkFetcher::StartNetworkRequest() {
|
| + TRACE_EVENT_ASYNC_BEGIN1("mojo_shell", "NetworkFetcher::NetworkRequest", this,
|
| + "url", url_.spec());
|
| network_service_->CreateURLLoader(GetProxy(&url_loader_));
|
| - url_loader_->Start(request.Pass(),
|
| + url_loader_->Start(GetRequest(url_, disable_cache_),
|
| base::Bind(&NetworkFetcher::OnLoadComplete,
|
| - weak_ptr_factory_.GetWeakPtr(), request_type));
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| }
|
|
|
| -void NetworkFetcher::OnLoadComplete(RequestType request_type,
|
| - mojo::URLResponsePtr response) {
|
| +void NetworkFetcher::OnLoadComplete(mojo::URLResponsePtr response) {
|
| TRACE_EVENT_ASYNC_END0("mojo_shell", "NetworkFetcher::NetworkRequest", this);
|
| - scoped_ptr<Fetcher> owner(this);
|
| if (response->error) {
|
| LOG(ERROR) << "Error (" << response->error->code << ": "
|
| << response->error->description << ") while fetching "
|
| << response->url;
|
| - if (request_type == FROM_NETWORK) {
|
| - StartNetworkRequest(FROM_CACHE);
|
| - } else {
|
| - loader_callback_.Run(nullptr);
|
| - }
|
| + loader_callback_.Run(nullptr);
|
| + delete this;
|
| return;
|
| }
|
|
|
| @@ -184,11 +256,54 @@ void NetworkFetcher::OnLoadComplete(RequestType request_type,
|
| << response->status_line << "): "
|
| << "while fetching " << response->url;
|
| loader_callback_.Run(nullptr);
|
| + delete this;
|
| return;
|
| }
|
|
|
| - response_ = response.Pass();
|
| - loader_callback_.Run(owner.Pass());
|
| + if (!response->redirect_url.is_null()) {
|
| + response_ = response.Pass();
|
| + loader_callback_.Run(make_scoped_ptr(this));
|
| + return;
|
| + }
|
| +
|
| + url_response_disk_cache_->UpdateAndGet(
|
| + response.Pass(), base::Bind(&NetworkFetcher::OnFileSavedToCache,
|
| + weak_ptr_factory_.GetWeakPtr()));
|
| +}
|
| +
|
| +void NetworkFetcher::OnFileSavedToCache(mojo::Array<uint8_t> path_as_array,
|
| + mojo::Array<uint8_t> cache_dir) {
|
| + if (!path_as_array) {
|
| + LOG(WARNING) << "Error when retrieving content from cache for: "
|
| + << url_.spec();
|
| + loader_callback_.Run(nullptr);
|
| + delete this;
|
| + return;
|
| + }
|
| + LoadFromCache(false);
|
| +}
|
| +
|
| +void NetworkFetcher::RecordCacheToURLMapping(const base::FilePath& path,
|
| + const GURL& url) {
|
| + // This is used to extract symbols on android.
|
| + // TODO(eseidel): All users of this log should move to using the map file.
|
| + LOG(INFO) << "Caching mojo app " << url << " at " << path.value();
|
| +
|
| + base::FilePath temp_dir;
|
| + base::GetTempDir(&temp_dir);
|
| + base::ProcessId pid = base::Process::Current().Pid();
|
| + std::string map_name = base::StringPrintf("mojo_shell.%d.maps", pid);
|
| + base::FilePath map_path = temp_dir.Append(map_name);
|
| +
|
| + // TODO(eseidel): Paths or URLs with spaces will need quoting.
|
| + std::string map_entry =
|
| + base::StringPrintf("%s %s\n", path.value().c_str(), url.spec().c_str());
|
| + // TODO(eseidel): AppendToFile is missing O_CREAT, crbug.com/450696
|
| + if (!PathExists(map_path)) {
|
| + base::WriteFile(map_path, map_entry.data(), map_entry.length());
|
| + } else {
|
| + base::AppendToFile(map_path, map_entry.data(), map_entry.length());
|
| + }
|
| }
|
|
|
| } // namespace shell
|
|
|