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"
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;
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
);
65 if (!GURL(upload_url_
).is_valid())
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();
75 bool success
= (response_code
== kHttpResponseOk
);
77 source
->GetResponseAsString(&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
));
89 void TraceCrashServiceUploader::OnURLFetchUploadProgress(
90 const net::URLFetcher
* source
,
93 DCHECK(url_fetcher_
.get());
95 LOG(WARNING
) << "Upload progress: " << current
<< " of " << total
;
97 if (progress_callback_
.is_null())
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
,
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");
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";
148 #error Platform not supported.
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());
158 if (product_components
.size() == 2U) {
159 version
= product_components
[1];
164 if (url_fetcher_
.get()) {
165 OnUploadError("Already uploading.");
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.");
178 compressed_contents
=
179 std::string(compressed_buffer
.get(), compressed_bytes
);
181 if (file_contents
.size() >= kMaxUploadBytes
) {
182 OnUploadError("File is too large to upload.");
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
, "",
214 net::AddMultipartValueForUpload("ver", version
+ "-trace", kMultipartBoundary
,
216 net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary
, "",
218 net::AddMultipartValueForUpload("type", "trace", kMultipartBoundary
, "",
220 // No minidump means no need for crash to process the report.
221 net::AddMultipartValueForUpload("should_process", "false", kMultipartBoundary
,
224 for (base::DictionaryValue::Iterator
it(*metadata
); !it
.IsAtEnd();
227 if (!it
.value().GetAsString(&value
)) {
228 if (!base::JSONWriter::Write(it
.value(), &value
))
232 net::AddMultipartValueForUpload(it
.key(), value
, kMultipartBoundary
, "",
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
,
260 int* compressed_bytes
) {
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.
267 8, // memLevel = 8 is default.
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
);
282 *compressed_bytes
= max_compressed_bytes
- stream
.avail_out
;
284 LOG(WARNING
) << "input size: " << input
.size()
285 << ", output size: " << *compressed_bytes
;
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
);
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();