Shutdown RenderWidgetHost owned by RenderFrameHost when destroying the RenderFrameHost.
[chromium-blink-merge.git] / mojo / shell / network_fetcher.cc
blob4de4d5d2a7894861ef57b734c048bafda1d57263
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
3 // found in the LICENSE file.
5 #include "mojo/shell/network_fetcher.h"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/files/file.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/process/process.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/trace_event/trace_event.h"
20 #include "crypto/secure_hash.h"
21 #include "crypto/sha2.h"
22 #include "mojo/common/common_type_converters.h"
23 #include "mojo/common/data_pipe_utils.h"
24 #include "mojo/common/url_type_converters.h"
25 #include "mojo/services/network/public/interfaces/network_service.mojom.h"
26 #include "mojo/shell/data_pipe_peek.h"
27 #include "mojo/shell/switches.h"
29 namespace mojo {
30 namespace shell {
32 NetworkFetcher::NetworkFetcher(bool disable_cache,
33 const GURL& url,
34 NetworkService* network_service,
35 const FetchCallback& loader_callback)
36 : Fetcher(loader_callback),
37 disable_cache_(false),
38 url_(url),
39 weak_ptr_factory_(this) {
40 StartNetworkRequest(url, network_service);
43 NetworkFetcher::~NetworkFetcher() {
46 const GURL& NetworkFetcher::GetURL() const {
47 return url_;
50 GURL NetworkFetcher::GetRedirectURL() const {
51 if (!response_)
52 return GURL::EmptyGURL();
54 if (response_->redirect_url.is_null())
55 return GURL::EmptyGURL();
57 return GURL(response_->redirect_url);
60 URLResponsePtr NetworkFetcher::AsURLResponse(base::TaskRunner* task_runner,
61 uint32_t skip) {
62 if (skip != 0) {
63 MojoResult result = ReadDataRaw(
64 response_->body.get(), nullptr, &skip,
65 MOJO_READ_DATA_FLAG_ALL_OR_NONE | MOJO_READ_DATA_FLAG_DISCARD);
66 DCHECK_EQ(result, MOJO_RESULT_OK);
68 return response_.Pass();
71 void NetworkFetcher::RecordCacheToURLMapping(const base::FilePath& path,
72 const GURL& url) {
73 // This is used to extract symbols on android.
74 // TODO(eseidel): All users of this log should move to using the map file.
75 VLOG(1) << "Caching mojo app " << url << " at " << path.value();
77 base::FilePath temp_dir;
78 base::GetTempDir(&temp_dir);
79 base::ProcessId pid = base::Process::Current().Pid();
80 std::string map_name = base::StringPrintf("mojo_shell.%d.maps", pid);
81 base::FilePath map_path = temp_dir.AppendASCII(map_name);
83 // TODO(eseidel): Paths or URLs with spaces will need quoting.
84 std::string map_entry =
85 base::StringPrintf("%s %s\n", path.value().c_str(), url.spec().c_str());
86 // TODO(eseidel): AppendToFile is missing O_CREAT, crbug.com/450696
87 if (!PathExists(map_path)) {
88 base::WriteFile(map_path, map_entry.data(),
89 static_cast<int>(map_entry.length()));
90 } else {
91 base::AppendToFile(map_path, map_entry.data(),
92 static_cast<int>(map_entry.length()));
96 // For remote debugging, GDB needs to be, a apriori, aware of the filename a
97 // library will be loaded from. AppIds should be be both predictable and unique,
98 // but any hash would work. Currently we use sha256 from crypto/secure_hash.h
99 bool NetworkFetcher::ComputeAppId(const base::FilePath& path,
100 std::string* digest_string) {
101 scoped_ptr<crypto::SecureHash> ctx(
102 crypto::SecureHash::Create(crypto::SecureHash::SHA256));
103 base::File file(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
104 if (!file.IsValid()) {
105 LOG(ERROR) << "Failed to open " << path.value() << " for computing AppId";
106 return false;
108 char buf[1024];
109 while (file.IsValid()) {
110 int bytes_read = file.ReadAtCurrentPos(buf, sizeof(buf));
111 if (bytes_read == 0)
112 break;
113 ctx->Update(buf, bytes_read);
115 if (!file.IsValid()) {
116 LOG(ERROR) << "Error reading " << path.value();
117 return false;
119 // The output is really a vector of unit8, we're cheating by using a string.
120 std::string output(crypto::kSHA256Length, 0);
121 ctx->Finish(string_as_array(&output), output.size());
122 output = base::HexEncode(output.c_str(), output.size());
123 // Using lowercase for compatiblity with sha256sum output.
124 *digest_string = base::StringToLowerASCII(output);
125 return true;
128 bool NetworkFetcher::RenameToAppId(const GURL& url,
129 const base::FilePath& old_path,
130 base::FilePath* new_path) {
131 std::string app_id;
132 if (!ComputeAppId(old_path, &app_id))
133 return false;
135 // Using a hash of the url as a directory to prevent a race when the same
136 // bytes are downloaded from 2 different urls. In particular, if the same
137 // application is connected to twice concurrently with different query
138 // parameters, the directory will be different, which will prevent the
139 // collision.
140 std::string dirname = base::HexEncode(
141 crypto::SHA256HashString(url.spec()).data(), crypto::kSHA256Length);
143 base::FilePath temp_dir;
144 base::GetTempDir(&temp_dir);
145 base::FilePath app_dir = temp_dir.AppendASCII(dirname);
146 // The directory is leaked, because it can be reused at any time if the same
147 // application is downloaded. Deleting it would be racy. This is only
148 // happening when --predictable-app-filenames is used.
149 bool result = base::CreateDirectoryAndGetError(app_dir, nullptr);
150 DCHECK(result);
151 std::string unique_name = base::StringPrintf("%s.mojo", app_id.c_str());
152 *new_path = app_dir.AppendASCII(unique_name);
153 return base::Move(old_path, *new_path);
156 void NetworkFetcher::CopyCompleted(
157 base::Callback<void(const base::FilePath&, bool)> callback,
158 bool success) {
159 if (success) {
160 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
161 switches::kPredictableAppFilenames)) {
162 // The copy completed, now move to $TMP/$APP_ID.mojo before the dlopen.
163 success = false;
164 base::FilePath new_path;
165 if (RenameToAppId(url_, path_, &new_path)) {
166 if (base::PathExists(new_path)) {
167 path_ = new_path;
168 success = true;
174 if (success)
175 RecordCacheToURLMapping(path_, url_);
177 base::MessageLoop::current()->PostTask(FROM_HERE,
178 base::Bind(callback, path_, success));
181 void NetworkFetcher::AsPath(
182 base::TaskRunner* task_runner,
183 base::Callback<void(const base::FilePath&, bool)> callback) {
184 if (!path_.empty() || !response_) {
185 base::MessageLoop::current()->PostTask(
186 FROM_HERE, base::Bind(callback, path_, base::PathExists(path_)));
187 return;
190 base::CreateTemporaryFile(&path_);
191 common::CopyToFile(response_->body.Pass(), path_, task_runner,
192 base::Bind(&NetworkFetcher::CopyCompleted,
193 weak_ptr_factory_.GetWeakPtr(), callback));
196 std::string NetworkFetcher::MimeType() {
197 return response_->mime_type;
200 bool NetworkFetcher::HasMojoMagic() {
201 std::string magic;
202 return BlockingPeekNBytes(response_->body.get(), &magic, strlen(kMojoMagic),
203 kPeekTimeout) &&
204 magic == kMojoMagic;
207 bool NetworkFetcher::PeekFirstLine(std::string* line) {
208 return BlockingPeekLine(response_->body.get(), line, kMaxShebangLength,
209 kPeekTimeout);
212 void NetworkFetcher::StartNetworkRequest(const GURL& url,
213 NetworkService* network_service) {
214 TRACE_EVENT_ASYNC_BEGIN1("mojo_shell", "NetworkFetcher::NetworkRequest", this,
215 "url", url.spec());
216 URLRequestPtr request(URLRequest::New());
217 request->url = String::From(url);
218 request->auto_follow_redirects = false;
219 request->bypass_cache = disable_cache_;
221 network_service->CreateURLLoader(GetProxy(&url_loader_));
222 url_loader_->Start(request.Pass(),
223 base::Bind(&NetworkFetcher::OnLoadComplete,
224 weak_ptr_factory_.GetWeakPtr()));
227 void NetworkFetcher::OnLoadComplete(URLResponsePtr response) {
228 TRACE_EVENT_ASYNC_END0("mojo_shell", "NetworkFetcher::NetworkRequest", this);
229 scoped_ptr<Fetcher> owner(this);
230 if (response->error) {
231 LOG(ERROR) << "Error (" << response->error->code << ": "
232 << response->error->description << ") while fetching "
233 << response->url;
234 loader_callback_.Run(nullptr);
235 return;
238 if (response->status_code >= 400 && response->status_code < 600) {
239 LOG(ERROR) << "Error (" << response->status_code << ": "
240 << response->status_line << "): "
241 << "while fetching " << response->url;
242 loader_callback_.Run(nullptr);
243 return;
246 response_ = response.Pass();
247 loader_callback_.Run(owner.Pass());
250 } // namespace shell
251 } // namespace mojo