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.
4
5 #include "content/browser/tracing/trace_uploader.h"
6
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.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"
24 #include "url/gurl.h"
25
26 namespace content {
27 namespace {
28 const char kUploadContentType[] = "multipart/form-data";
29 const char kMultipartBoundary[] =
30 "----**--yradnuoBgoLtrapitluMklaTelgooG--**----";
31
32 const int kHttpResponseOk = 200;
33
34 } // namespace
35
TraceUploader(const std::string & product,const std::string & version,const std::string & upload_url,net::URLRequestContextGetter * request_context)36 TraceUploader::TraceUploader(const std::string& product,
37 const std::string& version,
38 const std::string& upload_url,
39 net::URLRequestContextGetter* request_context)
40 : product_(product),
41 version_(version),
42 upload_url_(upload_url),
43 request_context_(request_context) {
44 DCHECK(!product_.empty());
45 DCHECK(!version_.empty());
46 DCHECK(!upload_url_.empty());
47 }
48
~TraceUploader()49 TraceUploader::~TraceUploader() {
50 DCHECK_CURRENTLY_ON(BrowserThread::UI);
51 }
52
OnURLFetchComplete(const net::URLFetcher * source)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();
57 string report_id;
58 string error_message;
59 bool success = (response_code == kHttpResponseOk);
60 if (success) {
61 source->GetResponseAsString(&report_id);
62 } else {
63 error_message = "Uploading failed, response code: " +
64 base::IntToString(response_code);
65 }
66
67 BrowserThread::PostTask(
68 content::BrowserThread::UI,
69 FROM_HERE,
70 base::Bind(done_callback_, success, report_id, error_message));
71 url_fetcher_.reset();
72 }
73
OnURLFetchUploadProgress(const net::URLFetcher * source,int64 current,int64 total)74 void TraceUploader::OnURLFetchUploadProgress(
75 const net::URLFetcher* source, int64 current, int64 total) {
76 DCHECK(url_fetcher_.get());
77
78 LOG(WARNING) << "Upload progress: " << current << " of " << total;
79 BrowserThread::PostTask(
80 content::BrowserThread::UI,
81 FROM_HERE,
82 base::Bind(progress_callback_, current, total));
83 }
84
DoUpload(const std::string & file_contents,UploadProgressCallback progress_callback,UploadDoneCallback done_callback)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());
91
92 progress_callback_ = progress_callback;
93 done_callback_ = done_callback;
94
95 if (url_fetcher_.get()) {
96 OnUploadError("Already uploading.");
97 }
98
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.");
104 return;
105 }
106
107 std::string post_data;
108 SetupMultipart("trace.json.gz",
109 std::string(compressed_contents.get(), compressed_bytes),
110 &post_data);
111
112 content::BrowserThread::PostTask(
113 content::BrowserThread::UI, FROM_HERE,
114 base::Bind(&TraceUploader::CreateAndStartURLFetcher,
115 base::Unretained(this),
116 post_data));
117 }
118
OnUploadError(std::string error_message)119 void TraceUploader::OnUploadError(std::string error_message) {
120 LOG(ERROR) << error_message;
121 content::BrowserThread::PostTask(
122 content::BrowserThread::UI,
123 FROM_HERE,
124 base::Bind(done_callback_, false, "", error_message));
125 }
126
SetupMultipart(const std::string & trace_filename,const std::string & trace_contents,std::string * post_data)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, "",
131 post_data);
132 net::AddMultipartValueForUpload("ver", version_ + "-trace",
133 kMultipartBoundary, "", post_data);
134 net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary,
135 "", post_data);
136 net::AddMultipartValueForUpload("type", "trace", kMultipartBoundary,
137 "", post_data);
138 // No minidump means no need for crash to process the report.
139 net::AddMultipartValueForUpload("should_process", "false", kMultipartBoundary,
140 "", post_data);
141
142 AddTraceFile(trace_filename, trace_contents, post_data);
143
144 net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data);
145 }
146
AddTraceFile(const std::string & trace_filename,const std::string & trace_contents,std::string * 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");
160 }
161
Compress(std::string input,int max_compressed_bytes,char * compressed,int * compressed_bytes)162 bool TraceUploader::Compress(std::string input,
163 int max_compressed_bytes,
164 char* compressed,
165 int* compressed_bytes) {
166 DCHECK(compressed);
167 DCHECK(compressed_bytes);
168 z_stream stream = {0};
169 int result = deflateInit2(&stream,
170 Z_DEFAULT_COMPRESSION,
171 Z_DEFLATED,
172 // 16 is added to produce a gzip header + trailer.
173 MAX_WBITS + 16,
174 8, // memLevel = 8 is default.
175 Z_DEFAULT_STRATEGY);
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);
187
188 if (success)
189 *compressed_bytes = max_compressed_bytes - stream.avail_out;
190
191 LOG(WARNING) << "input size: " << input.size()
192 << ", output size: " << *compressed_bytes;
193 return success;
194 }
195
CreateAndStartURLFetcher(const std::string & post_data)196 void TraceUploader::CreateAndStartURLFetcher(const std::string& post_data) {
197 DCHECK_CURRENTLY_ON(BrowserThread::UI);
198 DCHECK(!url_fetcher_.get());
199
200 std::string content_type = kUploadContentType;
201 content_type.append("; boundary=");
202 content_type.append(kMultipartBoundary);
203
204 url_fetcher_.reset(
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();
209 }
210
211 } // namespace content
212