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/memory/shared_memory.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 "chrome/common/chrome_version_info.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/common/content_switches.h"
20 #include "net/base/mime_util.h"
21 #include "net/base/network_delegate.h"
22 #include "net/proxy/proxy_config.h"
23 #include "net/proxy/proxy_config_service.h"
24 #include "net/url_request/url_fetcher.h"
25 #include "net/url_request/url_request_context.h"
26 #include "net/url_request/url_request_context_builder.h"
27 #include "net/url_request/url_request_context_getter.h"
28 #include "third_party/zlib/zlib.h"
34 const char kUploadURL
[] = "https://clients2.google.com/cr/staging_report";
35 const char kUploadContentType
[] = "multipart/form-data";
36 const char kMultipartBoundary
[] =
37 "----**--yradnuoBgoLtrapitluMklaTelgooG--**----";
38 const int kHttpResponseOk
= 200;
42 TraceCrashServiceUploader::TraceCrashServiceUploader(
43 net::URLRequestContextGetter
* request_context
)
44 : request_context_(request_context
) {
45 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
48 TraceCrashServiceUploader::~TraceCrashServiceUploader() {
49 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
52 void TraceCrashServiceUploader::OnURLFetchComplete(
53 const net::URLFetcher
* source
) {
54 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
55 DCHECK_EQ(source
, url_fetcher_
.get());
56 int response_code
= source
->GetResponseCode();
58 bool success
= (response_code
== kHttpResponseOk
);
60 source
->GetResponseAsString(&feedback
);
63 "Uploading failed, response code: " + base::IntToString(response_code
);
66 content::BrowserThread::PostTask(
67 content::BrowserThread::UI
, FROM_HERE
,
68 base::Bind(done_callback_
, success
, feedback
));
72 void TraceCrashServiceUploader::OnURLFetchUploadProgress(
73 const net::URLFetcher
* source
,
76 DCHECK(url_fetcher_
.get());
78 LOG(WARNING
) << "Upload progress: " << current
<< " of " << total
;
79 content::BrowserThread::PostTask(
80 content::BrowserThread::UI
, FROM_HERE
,
81 base::Bind(progress_callback_
, current
, total
));
84 void TraceCrashServiceUploader::DoUpload(
85 const std::string
& file_contents
,
86 const UploadProgressCallback
& progress_callback
,
87 const UploadDoneCallback
& done_callback
) {
88 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
89 content::BrowserThread::PostTask(
90 content::BrowserThread::FILE, FROM_HERE
,
91 base::Bind(&TraceCrashServiceUploader::DoUploadOnFileThread
,
92 base::Unretained(this), file_contents
, progress_callback
,
96 void TraceCrashServiceUploader::DoUploadOnFileThread(
97 const std::string
& file_contents
,
98 const UploadProgressCallback
& progress_callback
,
99 const UploadDoneCallback
& done_callback
) {
100 DCHECK_CURRENTLY_ON(content::BrowserThread::FILE);
101 DCHECK(!url_fetcher_
.get());
103 progress_callback_
= progress_callback
;
104 done_callback_
= done_callback
;
106 const base::CommandLine
& command_line
=
107 *base::CommandLine::ForCurrentProcess();
108 std::string upload_url
= kUploadURL
;
109 if (command_line
.HasSwitch(switches::kTraceUploadURL
)) {
110 upload_url
= command_line
.GetSwitchValueASCII(switches::kTraceUploadURL
);
112 if (!GURL(upload_url
).is_valid())
115 if (upload_url
.empty()) {
116 OnUploadError("Upload URL empty or invalid");
121 const char product
[] = "Chrome";
122 #elif defined(OS_MACOSX)
123 const char product
[] = "Chrome_Mac";
124 #elif defined(OS_LINUX)
125 const char product
[] = "Chrome_Linux";
126 #elif defined(OS_ANDROID)
127 const char product
[] = "Chrome_Android";
128 #elif defined(OS_CHROMEOS)
129 const char product
[] = "Chrome_ChromeOS";
131 #error Platform not supported.
134 // VersionInfo::ProductNameAndVersionForUserAgent() returns a string like
135 // "Chrome/aa.bb.cc.dd", split out the part before the "/".
136 chrome::VersionInfo version_info
;
137 std::vector
<std::string
> product_components
;
138 base::SplitString(version_info
.ProductNameAndVersionForUserAgent(), '/',
139 &product_components
);
140 DCHECK_EQ(2U, product_components
.size());
142 if (product_components
.size() == 2U) {
143 version
= product_components
[1];
148 if (url_fetcher_
.get()) {
149 OnUploadError("Already uploading.");
153 scoped_ptr
<char[]> compressed_contents(new char[kMaxUploadBytes
]);
154 int compressed_bytes
;
155 if (!Compress(file_contents
, kMaxUploadBytes
, compressed_contents
.get(),
156 &compressed_bytes
)) {
157 OnUploadError("Compressing file failed.");
161 std::string post_data
;
162 SetupMultipart(product
, version
, "trace.json.gz",
163 std::string(compressed_contents
.get(), compressed_bytes
),
166 content::BrowserThread::PostTask(
167 content::BrowserThread::UI
, FROM_HERE
,
168 base::Bind(&TraceCrashServiceUploader::CreateAndStartURLFetcher
,
169 base::Unretained(this), upload_url
, post_data
));
172 void TraceCrashServiceUploader::OnUploadError(std::string error_message
) {
173 LOG(ERROR
) << error_message
;
174 content::BrowserThread::PostTask(
175 content::BrowserThread::UI
, FROM_HERE
,
176 base::Bind(done_callback_
, false, error_message
));
179 void TraceCrashServiceUploader::SetupMultipart(
180 const std::string
& product
,
181 const std::string
& version
,
182 const std::string
& trace_filename
,
183 const std::string
& trace_contents
,
184 std::string
* post_data
) {
185 net::AddMultipartValueForUpload("prod", product
, kMultipartBoundary
, "",
187 net::AddMultipartValueForUpload("ver", version
+ "-trace", kMultipartBoundary
,
189 net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary
, "",
191 net::AddMultipartValueForUpload("type", "trace", kMultipartBoundary
, "",
193 // No minidump means no need for crash to process the report.
194 net::AddMultipartValueForUpload("should_process", "false", kMultipartBoundary
,
197 AddTraceFile(trace_filename
, trace_contents
, post_data
);
199 net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary
, post_data
);
202 void TraceCrashServiceUploader::AddTraceFile(const std::string
& trace_filename
,
203 const std::string
& trace_contents
,
204 std::string
* post_data
) {
205 post_data
->append("--");
206 post_data
->append(kMultipartBoundary
);
207 post_data
->append("\r\n");
208 post_data
->append("Content-Disposition: form-data; name=\"trace\"");
209 post_data
->append("; filename=\"");
210 post_data
->append(trace_filename
);
211 post_data
->append("\"\r\n");
212 post_data
->append("Content-Type: application/gzip\r\n\r\n");
213 post_data
->append(trace_contents
);
214 post_data
->append("\r\n");
217 bool TraceCrashServiceUploader::Compress(std::string input
,
218 int max_compressed_bytes
,
220 int* compressed_bytes
) {
222 DCHECK(compressed_bytes
);
223 z_stream stream
= {0};
224 int result
= deflateInit2(&stream
, Z_DEFAULT_COMPRESSION
, Z_DEFLATED
,
225 // 16 is added to produce a gzip header + trailer.
227 8, // memLevel = 8 is default.
229 DCHECK_EQ(Z_OK
, result
);
230 stream
.next_in
= reinterpret_cast<uint8
*>(&input
[0]);
231 stream
.avail_in
= input
.size();
232 stream
.next_out
= reinterpret_cast<uint8
*>(compressed
);
233 stream
.avail_out
= max_compressed_bytes
;
234 // Do a one-shot compression. This will return Z_STREAM_END only if |output|
235 // is large enough to hold all compressed data.
236 result
= deflate(&stream
, Z_FINISH
);
237 bool success
= (result
== Z_STREAM_END
);
238 result
= deflateEnd(&stream
);
239 DCHECK(result
== Z_OK
|| result
== Z_DATA_ERROR
);
242 *compressed_bytes
= max_compressed_bytes
- stream
.avail_out
;
244 LOG(WARNING
) << "input size: " << input
.size()
245 << ", output size: " << *compressed_bytes
;
249 void TraceCrashServiceUploader::CreateAndStartURLFetcher(
250 const std::string
& upload_url
,
251 const std::string
& post_data
) {
252 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
253 DCHECK(!url_fetcher_
.get());
255 std::string content_type
= kUploadContentType
;
256 content_type
.append("; boundary=");
257 content_type
.append(kMultipartBoundary
);
260 net::URLFetcher::Create(GURL(upload_url
), net::URLFetcher::POST
, this));
261 url_fetcher_
->SetRequestContext(request_context_
);
262 url_fetcher_
->SetUploadData(content_type
, post_data
);
263 url_fetcher_
->Start();