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