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 "content/browser/tracing/trace_uploader.h"
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.h"
9 #include "base/memory/shared_memory.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/time/time.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "net/base/mime_util.h"
15 #include "net/base/network_delegate.h"
16 #include "net/proxy/proxy_config.h"
17 #include "net/proxy/proxy_config_service.h"
18 #include "net/url_request/url_fetcher.h"
19 #include "net/url_request/url_request_context.h"
20 #include "net/url_request/url_request_context_builder.h"
21 #include "net/url_request/url_request_context_getter.h"
22 #include "third_party/zlib/zlib.h"
29 const char kUploadContentType
[] = "multipart/form-data";
30 const char kMultipartBoundary
[] =
31 "----**--yradnuoBgoLtrapitluMklaTelgooG--**----";
33 const int kHttpResponseOk
= 200;
37 TraceUploader::TraceUploader(const std::string
& product
,
38 const std::string
& version
,
39 const std::string
& upload_url
,
40 net::URLRequestContextGetter
* request_context
)
43 upload_url_(upload_url
),
44 request_context_(request_context
) {
45 DCHECK(!product_
.empty());
46 DCHECK(!version_
.empty());
47 DCHECK(!upload_url_
.empty());
50 TraceUploader::~TraceUploader() {
51 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
54 void TraceUploader::OnURLFetchComplete(const net::URLFetcher
* source
) {
55 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
56 DCHECK_EQ(source
, url_fetcher_
.get());
57 int response_code
= source
->GetResponseCode();
60 bool success
= (response_code
== kHttpResponseOk
);
62 source
->GetResponseAsString(&report_id
);
64 error_message
= "Uploading failed, response code: " +
65 base::IntToString(response_code
);
68 BrowserThread::PostTask(
69 content::BrowserThread::UI
,
71 base::Bind(done_callback_
, success
, report_id
, error_message
));
75 void TraceUploader::OnURLFetchUploadProgress(
76 const net::URLFetcher
* source
, int64 current
, int64 total
) {
77 DCHECK(url_fetcher_
.get());
79 LOG(WARNING
) << "Upload progress: " << current
<< " of " << total
;
80 BrowserThread::PostTask(
81 content::BrowserThread::UI
,
83 base::Bind(progress_callback_
, current
, total
));
86 void TraceUploader::DoUpload(
87 const std::string
& file_contents
,
88 UploadProgressCallback progress_callback
,
89 UploadDoneCallback done_callback
) {
90 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
91 DCHECK(!url_fetcher_
.get());
93 progress_callback_
= progress_callback
;
94 done_callback_
= done_callback
;
96 if (url_fetcher_
.get()) {
97 OnUploadError("Already uploading.");
100 scoped_ptr
<char[]> compressed_contents(new char[kMaxUploadBytes
]);
101 int compressed_bytes
;
102 if (!Compress(file_contents
, kMaxUploadBytes
, compressed_contents
.get(),
103 &compressed_bytes
)) {
104 OnUploadError("Compressing file failed.");
108 std::string post_data
;
109 SetupMultipart("trace.json.gz",
110 std::string(compressed_contents
.get(), compressed_bytes
),
113 content::BrowserThread::PostTask(
114 content::BrowserThread::UI
, FROM_HERE
,
115 base::Bind(&TraceUploader::CreateAndStartURLFetcher
,
116 base::Unretained(this),
120 void TraceUploader::OnUploadError(std::string error_message
) {
121 LOG(ERROR
) << error_message
;
122 content::BrowserThread::PostTask(
123 content::BrowserThread::UI
,
125 base::Bind(done_callback_
, false, "", error_message
));
128 void TraceUploader::SetupMultipart(const std::string
& trace_filename
,
129 const std::string
& trace_contents
,
130 std::string
* post_data
) {
131 net::AddMultipartValueForUpload("prod", product_
, kMultipartBoundary
, "",
133 net::AddMultipartValueForUpload("ver", version_
+ "-trace",
134 kMultipartBoundary
, "", post_data
);
135 net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary
,
137 net::AddMultipartValueForUpload("type", "trace", kMultipartBoundary
,
139 // No minidump means no need for crash to process the report.
140 net::AddMultipartValueForUpload("should_process", "false", kMultipartBoundary
,
143 AddTraceFile(trace_filename
, trace_contents
, post_data
);
145 net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary
, post_data
);
148 void TraceUploader::AddTraceFile(const std::string
& trace_filename
,
149 const std::string
& trace_contents
,
150 std::string
* post_data
) {
151 post_data
->append("--");
152 post_data
->append(kMultipartBoundary
);
153 post_data
->append("\r\n");
154 post_data
->append("Content-Disposition: form-data; name=\"trace\"");
155 post_data
->append("; filename=\"");
156 post_data
->append(trace_filename
);
157 post_data
->append("\"\r\n");
158 post_data
->append("Content-Type: application/gzip\r\n\r\n");
159 post_data
->append(trace_contents
);
160 post_data
->append("\r\n");
163 bool TraceUploader::Compress(std::string input
,
164 int max_compressed_bytes
,
166 int* compressed_bytes
) {
168 DCHECK(compressed_bytes
);
169 z_stream stream
= {0};
170 int result
= deflateInit2(&stream
,
171 Z_DEFAULT_COMPRESSION
,
173 // 16 is added to produce a gzip header + trailer.
175 8, // memLevel = 8 is default.
177 DCHECK_EQ(Z_OK
, result
);
178 stream
.next_in
= reinterpret_cast<uint8
*>(&input
[0]);
179 stream
.avail_in
= input
.size();
180 stream
.next_out
= reinterpret_cast<uint8
*>(compressed
);
181 stream
.avail_out
= max_compressed_bytes
;
182 // Do a one-shot compression. This will return Z_STREAM_END only if |output|
183 // is large enough to hold all compressed data.
184 result
= deflate(&stream
, Z_FINISH
);
185 bool success
= (result
== Z_STREAM_END
);
186 result
= deflateEnd(&stream
);
187 DCHECK(result
== Z_OK
|| result
== Z_DATA_ERROR
);
190 *compressed_bytes
= max_compressed_bytes
- stream
.avail_out
;
192 LOG(WARNING
) << "input size: " << input
.size()
193 << ", output size: " << *compressed_bytes
;
197 void TraceUploader::CreateAndStartURLFetcher(const std::string
& post_data
) {
198 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
199 DCHECK(!url_fetcher_
.get());
201 std::string content_type
= kUploadContentType
;
202 content_type
.append("; boundary=");
203 content_type
.append(kMultipartBoundary
);
206 net::URLFetcher::Create(GURL(upload_url_
), net::URLFetcher::POST
, this));
207 url_fetcher_
->SetRequestContext(request_context_
);
208 url_fetcher_
->SetUploadData(content_type
, post_data
);
209 url_fetcher_
->Start();
212 } // namespace content