Add ICU message format support
[chromium-blink-merge.git] / chrome / browser / tracing / crash_service_uploader.cc
blobe92637d811038b00ad49d6736936aa3d696b0780
1 // Copyright 2014 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 "chrome/browser/tracing/crash_service_uploader.h"
7 #include "base/command_line.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/format_macros.h"
11 #include "base/json/json_writer.h"
12 #include "base/memory/shared_memory.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/time/time.h"
18 #include "components/version_info/version_info.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "content/public/common/content_switches.h"
21 #include "net/base/mime_util.h"
22 #include "net/base/network_delegate.h"
23 #include "net/proxy/proxy_config.h"
24 #include "net/proxy/proxy_config_service.h"
25 #include "net/url_request/url_fetcher.h"
26 #include "net/url_request/url_request_context.h"
27 #include "net/url_request/url_request_context_builder.h"
28 #include "net/url_request/url_request_context_getter.h"
29 #include "third_party/zlib/zlib.h"
30 #include "url/gurl.h"
32 using std::string;
34 namespace {
35 const char kUploadURL[] = "https://clients2.google.com/cr/staging_report";
36 const char kUploadContentType[] = "multipart/form-data";
37 const char kMultipartBoundary[] =
38 "----**--yradnuoBgoLtrapitluMklaTelgooG--**----";
39 const int kHttpResponseOk = 200;
41 } // namespace
43 TraceCrashServiceUploader::TraceCrashServiceUploader(
44 net::URLRequestContextGetter* request_context)
45 : request_context_(request_context) {
46 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
47 const base::CommandLine& command_line =
48 *base::CommandLine::ForCurrentProcess();
49 std::string upload_url = kUploadURL;
50 if (command_line.HasSwitch(switches::kTraceUploadURL)) {
51 upload_url = command_line.GetSwitchValueASCII(switches::kTraceUploadURL);
53 SetUploadURL(upload_url);
56 TraceCrashServiceUploader::~TraceCrashServiceUploader() {
57 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
60 void TraceCrashServiceUploader::SetUploadURL(const std::string& url) {
61 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
62 upload_url_ = url;
64 if (!GURL(upload_url_).is_valid())
65 upload_url_.clear();
68 void TraceCrashServiceUploader::OnURLFetchComplete(
69 const net::URLFetcher* source) {
70 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
71 DCHECK_EQ(source, url_fetcher_.get());
72 int response_code = source->GetResponseCode();
73 string feedback;
74 bool success = (response_code == kHttpResponseOk);
75 if (success) {
76 source->GetResponseAsString(&feedback);
77 } else {
78 feedback =
79 "Uploading failed, response code: " + base::IntToString(response_code);
82 content::BrowserThread::PostTask(
83 content::BrowserThread::UI, FROM_HERE,
84 base::Bind(done_callback_, success, feedback));
85 url_fetcher_.reset();
88 void TraceCrashServiceUploader::OnURLFetchUploadProgress(
89 const net::URLFetcher* source,
90 int64 current,
91 int64 total) {
92 DCHECK(url_fetcher_.get());
94 LOG(WARNING) << "Upload progress: " << current << " of " << total;
96 if (progress_callback_.is_null())
97 return;
98 content::BrowserThread::PostTask(
99 content::BrowserThread::UI, FROM_HERE,
100 base::Bind(progress_callback_, current, total));
103 void TraceCrashServiceUploader::DoUpload(
104 const std::string& file_contents,
105 UploadMode upload_mode,
106 scoped_ptr<base::DictionaryValue> metadata,
107 const UploadProgressCallback& progress_callback,
108 const UploadDoneCallback& done_callback) {
109 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
110 content::BrowserThread::PostTask(
111 content::BrowserThread::FILE, FROM_HERE,
112 base::Bind(&TraceCrashServiceUploader::DoUploadOnFileThread,
113 base::Unretained(this), file_contents, upload_mode,
114 upload_url_, base::Passed(metadata.Pass()), progress_callback,
115 done_callback));
118 void TraceCrashServiceUploader::DoUploadOnFileThread(
119 const std::string& file_contents,
120 UploadMode upload_mode,
121 const std::string& upload_url,
122 scoped_ptr<base::DictionaryValue> metadata,
123 const UploadProgressCallback& progress_callback,
124 const UploadDoneCallback& done_callback) {
125 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
126 DCHECK(!url_fetcher_.get());
128 progress_callback_ = progress_callback;
129 done_callback_ = done_callback;
131 if (upload_url.empty()) {
132 OnUploadError("Upload URL empty or invalid");
133 return;
136 #if defined(OS_WIN)
137 const char product[] = "Chrome";
138 #elif defined(OS_MACOSX)
139 const char product[] = "Chrome_Mac";
140 #elif defined(OS_LINUX)
141 const char product[] = "Chrome_Linux";
142 #elif defined(OS_ANDROID)
143 const char product[] = "Chrome_Android";
144 #elif defined(OS_CHROMEOS)
145 const char product[] = "Chrome_ChromeOS";
146 #else
147 #error Platform not supported.
148 #endif
150 // version_info::GetProductNameAndVersionForUserAgent() returns a string like
151 // "Chrome/aa.bb.cc.dd", split out the part before the "/".
152 std::vector<std::string> product_components = base::SplitString(
153 version_info::GetProductNameAndVersionForUserAgent(), "/",
154 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
155 DCHECK_EQ(2U, product_components.size());
156 std::string version;
157 if (product_components.size() == 2U) {
158 version = product_components[1];
159 } else {
160 version = "unknown";
163 if (url_fetcher_.get()) {
164 OnUploadError("Already uploading.");
165 return;
168 std::string compressed_contents;
169 if (upload_mode == COMPRESSED_UPLOAD) {
170 scoped_ptr<char[]> compressed_buffer(new char[kMaxUploadBytes]);
171 int compressed_bytes;
172 if (!Compress(file_contents, kMaxUploadBytes, compressed_buffer.get(),
173 &compressed_bytes)) {
174 OnUploadError("Compressing file failed.");
175 return;
177 compressed_contents =
178 std::string(compressed_buffer.get(), compressed_bytes);
179 } else {
180 if (file_contents.size() >= kMaxUploadBytes) {
181 OnUploadError("File is too large to upload.");
182 return;
184 compressed_contents = file_contents;
187 std::string post_data;
188 SetupMultipart(product, version, metadata.Pass(), "trace.json.gz",
189 compressed_contents, &post_data);
191 content::BrowserThread::PostTask(
192 content::BrowserThread::UI, FROM_HERE,
193 base::Bind(&TraceCrashServiceUploader::CreateAndStartURLFetcher,
194 base::Unretained(this), upload_url, post_data));
197 void TraceCrashServiceUploader::OnUploadError(std::string error_message) {
198 LOG(ERROR) << error_message;
199 content::BrowserThread::PostTask(
200 content::BrowserThread::UI, FROM_HERE,
201 base::Bind(done_callback_, false, error_message));
204 void TraceCrashServiceUploader::SetupMultipart(
205 const std::string& product,
206 const std::string& version,
207 scoped_ptr<base::DictionaryValue> metadata,
208 const std::string& trace_filename,
209 const std::string& trace_contents,
210 std::string* post_data) {
211 net::AddMultipartValueForUpload("prod", product, kMultipartBoundary, "",
212 post_data);
213 net::AddMultipartValueForUpload("ver", version + "-trace", kMultipartBoundary,
214 "", post_data);
215 net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary, "",
216 post_data);
217 net::AddMultipartValueForUpload("type", "trace", kMultipartBoundary, "",
218 post_data);
219 // No minidump means no need for crash to process the report.
220 net::AddMultipartValueForUpload("should_process", "false", kMultipartBoundary,
221 "", post_data);
222 if (metadata) {
223 for (base::DictionaryValue::Iterator it(*metadata); !it.IsAtEnd();
224 it.Advance()) {
225 std::string value;
226 if (!it.value().GetAsString(&value)) {
227 if (!base::JSONWriter::Write(it.value(), &value))
228 continue;
231 net::AddMultipartValueForUpload(it.key(), value, kMultipartBoundary, "",
232 post_data);
236 AddTraceFile(trace_filename, trace_contents, post_data);
238 net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data);
241 void TraceCrashServiceUploader::AddTraceFile(const std::string& trace_filename,
242 const std::string& trace_contents,
243 std::string* post_data) {
244 post_data->append("--");
245 post_data->append(kMultipartBoundary);
246 post_data->append("\r\n");
247 post_data->append("Content-Disposition: form-data; name=\"trace\"");
248 post_data->append("; filename=\"");
249 post_data->append(trace_filename);
250 post_data->append("\"\r\n");
251 post_data->append("Content-Type: application/gzip\r\n\r\n");
252 post_data->append(trace_contents);
253 post_data->append("\r\n");
256 bool TraceCrashServiceUploader::Compress(std::string input,
257 int max_compressed_bytes,
258 char* compressed,
259 int* compressed_bytes) {
260 DCHECK(compressed);
261 DCHECK(compressed_bytes);
262 z_stream stream = {0};
263 int result = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
264 // 16 is added to produce a gzip header + trailer.
265 MAX_WBITS + 16,
266 8, // memLevel = 8 is default.
267 Z_DEFAULT_STRATEGY);
268 DCHECK_EQ(Z_OK, result);
269 stream.next_in = reinterpret_cast<uint8*>(&input[0]);
270 stream.avail_in = input.size();
271 stream.next_out = reinterpret_cast<uint8*>(compressed);
272 stream.avail_out = max_compressed_bytes;
273 // Do a one-shot compression. This will return Z_STREAM_END only if |output|
274 // is large enough to hold all compressed data.
275 result = deflate(&stream, Z_FINISH);
276 bool success = (result == Z_STREAM_END);
277 result = deflateEnd(&stream);
278 DCHECK(result == Z_OK || result == Z_DATA_ERROR);
280 if (success)
281 *compressed_bytes = max_compressed_bytes - stream.avail_out;
283 LOG(WARNING) << "input size: " << input.size()
284 << ", output size: " << *compressed_bytes;
285 return success;
288 void TraceCrashServiceUploader::CreateAndStartURLFetcher(
289 const std::string& upload_url,
290 const std::string& post_data) {
291 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
292 DCHECK(!url_fetcher_.get());
294 std::string content_type = kUploadContentType;
295 content_type.append("; boundary=");
296 content_type.append(kMultipartBoundary);
298 url_fetcher_ =
299 net::URLFetcher::Create(GURL(upload_url), net::URLFetcher::POST, this);
300 url_fetcher_->SetRequestContext(request_context_);
301 url_fetcher_->SetUploadData(content_type, post_data);
302 url_fetcher_->Start();