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/strings/string_number_conversions.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/time/time.h"
17 #include "components/tracing/tracing_switches.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"
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;
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
);
64 if (!GURL(upload_url_
).is_valid())
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();
74 bool success
= (response_code
== kHttpResponseOk
);
76 source
->GetResponseAsString(&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
));
88 void TraceCrashServiceUploader::OnURLFetchUploadProgress(
89 const net::URLFetcher
* source
,
92 DCHECK(url_fetcher_
.get());
94 LOG(WARNING
) << "Upload progress: " << current
<< " of " << total
;
96 if (progress_callback_
.is_null())
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
,
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");
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";
147 #error Platform not supported.
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());
157 if (product_components
.size() == 2U) {
158 version
= product_components
[1];
163 if (url_fetcher_
.get()) {
164 OnUploadError("Already uploading.");
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.");
177 compressed_contents
=
178 std::string(compressed_buffer
.get(), compressed_bytes
);
180 if (file_contents
.size() >= kMaxUploadBytes
) {
181 OnUploadError("File is too large to upload.");
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
, "",
213 net::AddMultipartValueForUpload("ver", version
+ "-trace", kMultipartBoundary
,
215 net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary
, "",
217 net::AddMultipartValueForUpload("type", "trace", kMultipartBoundary
, "",
219 // No minidump means no need for crash to process the report.
220 net::AddMultipartValueForUpload("should_process", "false", kMultipartBoundary
,
223 for (base::DictionaryValue::Iterator
it(*metadata
); !it
.IsAtEnd();
226 if (!it
.value().GetAsString(&value
)) {
227 if (!base::JSONWriter::Write(it
.value(), &value
))
231 net::AddMultipartValueForUpload(it
.key(), value
, kMultipartBoundary
, "",
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
,
259 int* compressed_bytes
) {
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.
266 8, // memLevel = 8 is default.
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
);
281 *compressed_bytes
= max_compressed_bytes
- stream
.avail_out
;
283 LOG(WARNING
) << "input size: " << input
.size()
284 << ", output size: " << *compressed_bytes
;
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
);
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();