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/file_util.h"
8 #include "base/files/file_path.h"
9 #include "base/memory/shared_memory.h"
10 #include "base/path_service.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "net/base/mime_util.h"
16 #include "net/base/network_delegate.h"
17 #include "net/proxy/proxy_config.h"
18 #include "net/proxy/proxy_config_service.h"
19 #include "net/url_request/url_fetcher.h"
20 #include "net/url_request/url_request_context.h"
21 #include "net/url_request/url_request_context_builder.h"
22 #include "net/url_request/url_request_context_getter.h"
23 #include "third_party/zlib/zlib.h"
28 const char kUploadContentType
[] = "multipart/form-data";
29 const char kMultipartBoundary
[] =
30 "----**--yradnuoBgoLtrapitluMklaTelgooG--**----";
32 const int kHttpResponseOk
= 200;
36 TraceUploader::TraceUploader(const std::string
& product
,
37 const std::string
& version
,
38 const std::string
& upload_url
,
39 net::URLRequestContextGetter
* request_context
)
42 upload_url_(upload_url
),
43 request_context_(request_context
) {
44 DCHECK(!product_
.empty());
45 DCHECK(!version_
.empty());
46 DCHECK(!upload_url_
.empty());
49 TraceUploader::~TraceUploader() {
50 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
53 void TraceUploader::OnURLFetchComplete(const net::URLFetcher
* source
) {
54 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
55 DCHECK_EQ(source
, url_fetcher_
.get());
56 int response_code
= source
->GetResponseCode();
59 bool success
= (response_code
== kHttpResponseOk
);
61 source
->GetResponseAsString(&report_id
);
63 error_message
= "Uploading failed, response code: " +
64 base::IntToString(response_code
);
67 BrowserThread::PostTask(
68 content::BrowserThread::UI
,
70 base::Bind(done_callback_
, success
, report_id
, error_message
));
74 void TraceUploader::OnURLFetchUploadProgress(
75 const net::URLFetcher
* source
, int64 current
, int64 total
) {
76 DCHECK(url_fetcher_
.get());
78 LOG(WARNING
) << "Upload progress: " << current
<< " of " << total
;
79 BrowserThread::PostTask(
80 content::BrowserThread::UI
,
82 base::Bind(progress_callback_
, current
, total
));
85 void TraceUploader::DoUpload(
86 const std::string
& file_contents
,
87 UploadProgressCallback progress_callback
,
88 UploadDoneCallback done_callback
) {
89 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
90 DCHECK(!url_fetcher_
.get());
92 progress_callback_
= progress_callback
;
93 done_callback_
= done_callback
;
95 if (url_fetcher_
.get()) {
96 OnUploadError("Already uploading.");
99 scoped_ptr
<char[]> compressed_contents(new char[kMaxUploadBytes
]);
100 int compressed_bytes
;
101 if (!Compress(file_contents
, kMaxUploadBytes
, compressed_contents
.get(),
102 &compressed_bytes
)) {
103 OnUploadError("Compressing file failed.");
107 std::string post_data
;
108 SetupMultipart("trace.json.gz",
109 std::string(compressed_contents
.get(), compressed_bytes
),
112 content::BrowserThread::PostTask(
113 content::BrowserThread::UI
, FROM_HERE
,
114 base::Bind(&TraceUploader::CreateAndStartURLFetcher
,
115 base::Unretained(this),
119 void TraceUploader::OnUploadError(std::string error_message
) {
120 LOG(ERROR
) << error_message
;
121 content::BrowserThread::PostTask(
122 content::BrowserThread::UI
,
124 base::Bind(done_callback_
, false, "", error_message
));
127 void TraceUploader::SetupMultipart(const std::string
& trace_filename
,
128 const std::string
& trace_contents
,
129 std::string
* post_data
) {
130 net::AddMultipartValueForUpload("prod", product_
, kMultipartBoundary
, "",
132 net::AddMultipartValueForUpload("ver", version_
+ "-trace",
133 kMultipartBoundary
, "", post_data
);
134 net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary
,
136 net::AddMultipartValueForUpload("type", "trace", kMultipartBoundary
,
138 // No minidump means no need for crash to process the report.
139 net::AddMultipartValueForUpload("should_process", "false", kMultipartBoundary
,
142 AddTraceFile(trace_filename
, trace_contents
, post_data
);
144 net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary
, post_data
);
147 void TraceUploader::AddTraceFile(const std::string
& trace_filename
,
148 const std::string
& trace_contents
,
149 std::string
* post_data
) {
150 post_data
->append("--");
151 post_data
->append(kMultipartBoundary
);
152 post_data
->append("\r\n");
153 post_data
->append("Content-Disposition: form-data; name=\"trace\"");
154 post_data
->append("; filename=\"");
155 post_data
->append(trace_filename
);
156 post_data
->append("\"\r\n");
157 post_data
->append("Content-Type: application/gzip\r\n\r\n");
158 post_data
->append(trace_contents
);
159 post_data
->append("\r\n");
162 bool TraceUploader::Compress(std::string input
,
163 int max_compressed_bytes
,
165 int* compressed_bytes
) {
167 DCHECK(compressed_bytes
);
168 z_stream stream
= {0};
169 int result
= deflateInit2(&stream
,
170 Z_DEFAULT_COMPRESSION
,
172 // 16 is added to produce a gzip header + trailer.
174 8, // memLevel = 8 is default.
176 DCHECK_EQ(Z_OK
, result
);
177 stream
.next_in
= reinterpret_cast<uint8
*>(&input
[0]);
178 stream
.avail_in
= input
.size();
179 stream
.next_out
= reinterpret_cast<uint8
*>(compressed
);
180 stream
.avail_out
= max_compressed_bytes
;
181 // Do a one-shot compression. This will return Z_STREAM_END only if |output|
182 // is large enough to hold all compressed data.
183 result
= deflate(&stream
, Z_FINISH
);
184 bool success
= (result
== Z_STREAM_END
);
185 result
= deflateEnd(&stream
);
186 DCHECK(result
== Z_OK
|| result
== Z_DATA_ERROR
);
189 *compressed_bytes
= max_compressed_bytes
- stream
.avail_out
;
191 LOG(WARNING
) << "input size: " << input
.size()
192 << ", output size: " << *compressed_bytes
;
196 void TraceUploader::CreateAndStartURLFetcher(const std::string
& post_data
) {
197 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
198 DCHECK(!url_fetcher_
.get());
200 std::string content_type
= kUploadContentType
;
201 content_type
.append("; boundary=");
202 content_type
.append(kMultipartBoundary
);
205 net::URLFetcher::Create(GURL(upload_url_
), net::URLFetcher::POST
, this));
206 url_fetcher_
->SetRequestContext(request_context_
);
207 url_fetcher_
->SetUploadData(content_type
, post_data
);
208 url_fetcher_
->Start();
211 } // namespace content