Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(126)

Side by Side Diff: shell/application_manager/network_fetcher.cc

Issue 1276073004: Offline By Default (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Rename MoveIntoDir Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "shell/application_manager/network_fetcher.h" 5 #include "shell/application_manager/network_fetcher.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/command_line.h" 8 #include "base/command_line.h"
9 #include "base/files/file.h" 9 #include "base/files/file.h"
10 #include "base/files/file_path.h" 10 #include "base/files/file_path.h"
(...skipping 14 matching lines...) Expand all
25 namespace shell { 25 namespace shell {
26 26
27 namespace { 27 namespace {
28 #if defined(OS_LINUX) 28 #if defined(OS_LINUX)
29 char kArchitecture[] = "linux-x64"; 29 char kArchitecture[] = "linux-x64";
30 #elif defined(OS_ANDROID) 30 #elif defined(OS_ANDROID)
31 char kArchitecture[] = "android-arm"; 31 char kArchitecture[] = "android-arm";
32 #else 32 #else
33 #error "Unsupported." 33 #error "Unsupported."
34 #endif 34 #endif
35
36 // The delay to wait before trying to update an application after having served
37 // it from the cache.
38 const uint32_t kUpdateApplicationDelayInSeconds = 60;
39
40 base::FilePath ToFilePath(const mojo::Array<uint8_t>& array) {
41 return base::FilePath(
42 std::string(reinterpret_cast<const char*>(&array.front()), array.size()));
43 }
44
45 void IgnoreResult(bool result) {}
46
47 mojo::URLRequestPtr GetRequest(const GURL& url, bool disable_cache) {
48 mojo::URLRequestPtr request(mojo::URLRequest::New());
49 request->url = mojo::String::From(url);
50 request->auto_follow_redirects = false;
51 if (disable_cache)
52 request->cache_mode = mojo::URLRequest::CACHE_MODE_BYPASS_CACHE;
53 auto header = mojo::HttpHeader::New();
ppi 2015/09/08 15:46:26 this should probably be called architecture_header
qsr 2015/09/11 15:47:58 Done.
54 header->name = "X-Architecture";
55 header->value = kArchitecture;
56 mojo::Array<mojo::HttpHeaderPtr> headers;
57 headers.push_back(header.Pass());
58 request->headers = headers.Pass();
59
60 return request.Pass();
61 }
62
ppi 2015/09/08 15:46:26 Please document the lifetime.
qsr 2015/09/11 15:47:58 Done.
63 class ApplicationUpdater : public base::MessageLoop::DestructionObserver {
64 public:
65 ApplicationUpdater(const GURL& url,
66 mojo::URLResponseDiskCache* url_response_disk_cache,
67 mojo::NetworkService* network_service);
ppi 2015/09/08 15:46:26 Why not take the delay as an argument? I can imagi
qsr 2015/09/11 15:47:58 Done.
68 ~ApplicationUpdater() override;
69
70 private:
71 // DestructionObserver
72 void WillDestroyCurrentMessageLoop() override;
73
74 void UpdateApplication();
75 void OnLoadComplete(mojo::URLResponsePtr response);
76
77 GURL url_;
78 mojo::URLResponseDiskCache* url_response_disk_cache_;
79 mojo::NetworkService* network_service_;
80 mojo::URLLoaderPtr url_loader_;
35 }; 81 };
36 82
83 ApplicationUpdater::ApplicationUpdater(
84 const GURL& url,
85 mojo::URLResponseDiskCache* url_response_disk_cache,
86 mojo::NetworkService* network_service)
87 : url_(url),
88 url_response_disk_cache_(url_response_disk_cache),
89 network_service_(network_service) {
90 base::MessageLoop::current()->AddDestructionObserver(this);
91 base::MessageLoop::current()->PostDelayedTask(
92 FROM_HERE, base::Bind(&ApplicationUpdater::UpdateApplication,
93 base::Unretained(this)),
94 base::TimeDelta::FromSeconds(kUpdateApplicationDelayInSeconds));
95 }
96
97 ApplicationUpdater::~ApplicationUpdater() {
98 base::MessageLoop::current()->RemoveDestructionObserver(this);
99 }
100
101 void ApplicationUpdater::WillDestroyCurrentMessageLoop() {
102 delete this;
103 }
104
105 void ApplicationUpdater::UpdateApplication() {
106 network_service_->CreateURLLoader(GetProxy(&url_loader_));
107 url_loader_->Start(
108 GetRequest(url_, false),
109 base::Bind(&ApplicationUpdater::OnLoadComplete, base::Unretained(this)));
110 }
111
112 void ApplicationUpdater::OnLoadComplete(mojo::URLResponsePtr response) {
113 url_response_disk_cache_->Update(response.Pass());
114 delete this;
115 }
116
117 } // namespace
118
37 NetworkFetcher::NetworkFetcher( 119 NetworkFetcher::NetworkFetcher(
38 bool disable_cache, 120 bool disable_cache,
39 const GURL& url, 121 const GURL& url,
40 mojo::URLResponseDiskCache* url_response_disk_cache, 122 mojo::URLResponseDiskCache* url_response_disk_cache,
41 mojo::NetworkService* network_service, 123 mojo::NetworkService* network_service,
42 const FetchCallback& loader_callback) 124 const FetchCallback& loader_callback)
43 : Fetcher(loader_callback), 125 : Fetcher(loader_callback),
44 disable_cache_(disable_cache), 126 disable_cache_(disable_cache),
45 url_(url), 127 url_(url),
46 url_response_disk_cache_(url_response_disk_cache), 128 url_response_disk_cache_(url_response_disk_cache),
47 network_service_(network_service), 129 network_service_(network_service),
48 weak_ptr_factory_(this) { 130 weak_ptr_factory_(this) {
49 StartNetworkRequest(FROM_NETWORK); 131 if (CanLoadDirectlyFromCache()) {
132 LoadFromCache(true);
133 } else {
134 StartNetworkRequest();
135 }
50 } 136 }
51 137
52 NetworkFetcher::~NetworkFetcher() { 138 NetworkFetcher::~NetworkFetcher() {
53 } 139 }
54 140
55 const GURL& NetworkFetcher::GetURL() const { 141 const GURL& NetworkFetcher::GetURL() const {
56 return url_; 142 return url_;
57 } 143 }
58 144
59 GURL NetworkFetcher::GetRedirectURL() const { 145 GURL NetworkFetcher::GetRedirectURL() const {
60 if (!response_) 146 if (!response_)
61 return GURL::EmptyGURL(); 147 return GURL::EmptyGURL();
62 148
63 if (response_->redirect_url.is_null()) 149 if (response_->redirect_url.is_null())
64 return GURL::EmptyGURL(); 150 return GURL::EmptyGURL();
65 151
66 return GURL(response_->redirect_url); 152 return GURL(response_->redirect_url);
67 } 153 }
68 154
69 mojo::URLResponsePtr NetworkFetcher::AsURLResponse( 155 mojo::URLResponsePtr NetworkFetcher::AsURLResponse(
70 base::TaskRunner* task_runner, 156 base::TaskRunner* task_runner,
71 uint32_t skip) { 157 uint32_t skip) {
72 if (skip != 0) { 158 DCHECK(response_);
73 MojoResult result = ReadDataRaw( 159 DCHECK(!path_.empty());
74 response_->body.get(), nullptr, &skip, 160 mojo::DataPipe data_pipe;
75 MOJO_READ_DATA_FLAG_ALL_OR_NONE | MOJO_READ_DATA_FLAG_DISCARD); 161 response_->body = data_pipe.consumer_handle.Pass();
76 DCHECK_EQ(result, MOJO_RESULT_OK); 162 mojo::common::CopyFromFile(path_, data_pipe.producer_handle.Pass(), skip,
77 } 163 task_runner, base::Bind(&IgnoreResult));
78 return response_.Pass(); 164 return response_.Pass();
79 } 165 }
80 166
81 void NetworkFetcher::RecordCacheToURLMapping(const base::FilePath& path,
82 const GURL& url) {
83 // This is used to extract symbols on android.
84 // TODO(eseidel): All users of this log should move to using the map file.
85 LOG(INFO) << "Caching mojo app " << url << " at " << path.value();
86
87 base::FilePath temp_dir;
88 base::GetTempDir(&temp_dir);
89 base::ProcessId pid = base::Process::Current().Pid();
90 std::string map_name = base::StringPrintf("mojo_shell.%d.maps", pid);
91 base::FilePath map_path = temp_dir.Append(map_name);
92
93 // TODO(eseidel): Paths or URLs with spaces will need quoting.
94 std::string map_entry =
95 base::StringPrintf("%s %s\n", path.value().c_str(), url.spec().c_str());
96 // TODO(eseidel): AppendToFile is missing O_CREAT, crbug.com/450696
97 if (!PathExists(map_path))
98 base::WriteFile(map_path, map_entry.data(), map_entry.length());
99 else
100 base::AppendToFile(map_path, map_entry.data(), map_entry.length());
101 }
102
103 void NetworkFetcher::OnFileRetrievedFromCache(
104 base::Callback<void(const base::FilePath&, bool)> callback,
105 mojo::Array<uint8_t> path_as_array,
106 mojo::Array<uint8_t> cache_dir) {
107 bool success = !path_as_array.is_null();
108 if (success) {
109 path_ = base::FilePath(std::string(
110 reinterpret_cast<char*>(&path_as_array.front()), path_as_array.size()));
111 RecordCacheToURLMapping(path_, url_);
112 }
113
114 base::MessageLoop::current()->PostTask(FROM_HERE,
115 base::Bind(callback, path_, success));
116 }
117
118 void NetworkFetcher::AsPath( 167 void NetworkFetcher::AsPath(
119 base::TaskRunner* task_runner, 168 base::TaskRunner* task_runner,
120 base::Callback<void(const base::FilePath&, bool)> callback) { 169 base::Callback<void(const base::FilePath&, bool)> callback) {
121 // This should only called once, when we have a response. 170 // This should only called once, when we have a response.
122 DCHECK(response_.get()); 171 DCHECK(response_.get());
123 172
124 url_response_disk_cache_->GetFile( 173 base::MessageLoop::current()->PostTask(
125 response_.Pass(), base::Bind(&NetworkFetcher::OnFileRetrievedFromCache, 174 FROM_HERE, base::Bind(callback, path_, base::PathExists(path_)));
126 weak_ptr_factory_.GetWeakPtr(), callback)); 175 response_.reset();
176 return;
127 } 177 }
128 178
129 std::string NetworkFetcher::MimeType() { 179 std::string NetworkFetcher::MimeType() {
130 return response_->mime_type; 180 return response_->mime_type;
131 } 181 }
132 182
133 bool NetworkFetcher::HasMojoMagic() { 183 bool NetworkFetcher::HasMojoMagic() {
134 std::string magic; 184 return Fetcher::HasMojoMagic(path_);
135 return BlockingPeekNBytes(response_->body.get(), &magic, strlen(kMojoMagic),
136 kPeekTimeout) &&
137 magic == kMojoMagic;
138 } 185 }
139 186
140 bool NetworkFetcher::PeekFirstLine(std::string* line) { 187 bool NetworkFetcher::PeekFirstLine(std::string* line) {
141 return BlockingPeekLine(response_->body.get(), line, kMaxShebangLength, 188 return Fetcher::PeekFirstLine(path_, line);
142 kPeekTimeout);
143 } 189 }
144 190
145 void NetworkFetcher::StartNetworkRequest(RequestType request_type) { 191 bool NetworkFetcher::CanLoadDirectlyFromCache() {
192 if (disable_cache_)
193 return false;
194
195 const std::string& host = url_.host();
196 return !(host == "localhost" || host == "127.0.0.1" || host == "[::1]");
197 }
198
199 void NetworkFetcher::LoadFromCache(bool initial) {
200 url_response_disk_cache_->Get(
201 mojo::String::From(url_),
202 base::Bind(&NetworkFetcher::OnCachedResponseReceived,
203 base::Unretained(this), initial));
204 }
205
206 void NetworkFetcher::OnCachedResponseReceived(
207 bool initial,
208 mojo::URLResponsePtr response,
209 mojo::Array<uint8_t> path_as_array,
210 mojo::Array<uint8_t> cache_dir) {
211 if (!response) {
212 // Not in cache, loading from net.
213 StartNetworkRequest();
214 return;
215 }
216 if (initial) {
217 // The response has been found in the cache. Plan updating the application.
218 new ApplicationUpdater(url_, url_response_disk_cache_, network_service_);
219 }
220 response_ = response.Pass();
221 path_ = ToFilePath(path_as_array);
222 RecordCacheToURLMapping(path_, url_);
223 loader_callback_.Run(make_scoped_ptr(this));
224 }
225
226 void NetworkFetcher::StartNetworkRequest() {
146 TRACE_EVENT_ASYNC_BEGIN1("mojo_shell", "NetworkFetcher::NetworkRequest", this, 227 TRACE_EVENT_ASYNC_BEGIN1("mojo_shell", "NetworkFetcher::NetworkRequest", this,
147 "url", url_.spec()); 228 "url", url_.spec());
148 mojo::URLRequestPtr request(mojo::URLRequest::New());
149 request->url = mojo::String::From(url_);
150 request->auto_follow_redirects = false;
151 if (disable_cache_)
152 request->cache_mode = mojo::URLRequest::CACHE_MODE_BYPASS_CACHE;
153 auto header = mojo::HttpHeader::New();
154 header->name = "X-Architecture";
155 header->value = kArchitecture;
156 mojo::Array<mojo::HttpHeaderPtr> headers;
157 headers.push_back(header.Pass());
158 request->headers = headers.Pass();
159
160 network_service_->CreateURLLoader(GetProxy(&url_loader_)); 229 network_service_->CreateURLLoader(GetProxy(&url_loader_));
161 url_loader_->Start(request.Pass(), 230 url_loader_->Start(GetRequest(url_, disable_cache_),
162 base::Bind(&NetworkFetcher::OnLoadComplete, 231 base::Bind(&NetworkFetcher::OnLoadComplete,
163 weak_ptr_factory_.GetWeakPtr(), request_type)); 232 weak_ptr_factory_.GetWeakPtr()));
164 } 233 }
165 234
166 void NetworkFetcher::OnLoadComplete(RequestType request_type, 235 void NetworkFetcher::OnLoadComplete(mojo::URLResponsePtr response) {
167 mojo::URLResponsePtr response) {
168 TRACE_EVENT_ASYNC_END0("mojo_shell", "NetworkFetcher::NetworkRequest", this); 236 TRACE_EVENT_ASYNC_END0("mojo_shell", "NetworkFetcher::NetworkRequest", this);
169 scoped_ptr<Fetcher> owner(this);
170 if (response->error) { 237 if (response->error) {
171 LOG(ERROR) << "Error (" << response->error->code << ": " 238 LOG(ERROR) << "Error (" << response->error->code << ": "
172 << response->error->description << ") while fetching " 239 << response->error->description << ") while fetching "
173 << response->url; 240 << response->url;
174 if (request_type == FROM_NETWORK) { 241 loader_callback_.Run(nullptr);
175 StartNetworkRequest(FROM_CACHE); 242 delete this;
176 } else {
177 loader_callback_.Run(nullptr);
178 }
179 return; 243 return;
180 } 244 }
181 245
182 if (response->status_code >= 400 && response->status_code < 600) { 246 if (response->status_code >= 400 && response->status_code < 600) {
183 LOG(ERROR) << "Error (" << response->status_code << ": " 247 LOG(ERROR) << "Error (" << response->status_code << ": "
184 << response->status_line << "): " 248 << response->status_line << "): "
185 << "while fetching " << response->url; 249 << "while fetching " << response->url;
186 loader_callback_.Run(nullptr); 250 loader_callback_.Run(nullptr);
251 delete this;
187 return; 252 return;
188 } 253 }
189 254
190 response_ = response.Pass(); 255 if (!response->redirect_url.is_null()) {
191 loader_callback_.Run(owner.Pass()); 256 response_ = response.Pass();
257 loader_callback_.Run(make_scoped_ptr(this));
258 return;
259 }
260
261 url_response_disk_cache_->UpdateAndGet(
262 response.Pass(), base::Bind(&NetworkFetcher::OnFileSavedToCache,
263 weak_ptr_factory_.GetWeakPtr()));
264 }
265
266 void NetworkFetcher::OnFileSavedToCache(mojo::Array<uint8_t> path_as_array,
267 mojo::Array<uint8_t> cache_dir) {
268 if (!path_as_array) {
269 LOG(WARNING) << "Error when retrieving content from cache for: "
270 << url_.spec();
271 loader_callback_.Run(nullptr);
272 delete this;
273 return;
274 }
275 LoadFromCache(false);
276 }
277
278 void NetworkFetcher::RecordCacheToURLMapping(const base::FilePath& path,
279 const GURL& url) {
280 // This is used to extract symbols on android.
281 // TODO(eseidel): All users of this log should move to using the map file.
282 LOG(INFO) << "Caching mojo app " << url << " at " << path.value();
283
284 base::FilePath temp_dir;
285 base::GetTempDir(&temp_dir);
286 base::ProcessId pid = base::Process::Current().Pid();
287 std::string map_name = base::StringPrintf("mojo_shell.%d.maps", pid);
288 base::FilePath map_path = temp_dir.Append(map_name);
289
290 // TODO(eseidel): Paths or URLs with spaces will need quoting.
291 std::string map_entry =
292 base::StringPrintf("%s %s\n", path.value().c_str(), url.spec().c_str());
293 // TODO(eseidel): AppendToFile is missing O_CREAT, crbug.com/450696
294 if (!PathExists(map_path))
ppi 2015/09/08 15:46:26 As you are using the if-else style with braces in
qsr 2015/09/11 15:47:58 Done.
295 base::WriteFile(map_path, map_entry.data(), map_entry.length());
296 else
297 base::AppendToFile(map_path, map_entry.data(), map_entry.length());
192 } 298 }
193 299
194 } // namespace shell 300 } // namespace shell
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698