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"
27 const char kUploadContentType[] = "multipart/form-data";
28 const char kMultipartBoundary[] =
29 "----**--yradnuoBgoLtrapitluMklaTelgooG--**----";
31 const int kHttpResponseOk = 200;
35 TraceUploader::TraceUploader(const std::string& product,
36 const std::string& version,
37 const std::string& upload_url,
38 net::URLRequestContextGetter* request_context)
41 upload_url_(upload_url),
42 request_context_(request_context) {
43 DCHECK(!product_.empty());
44 DCHECK(!version_.empty());
45 DCHECK(!upload_url_.empty());
48 TraceUploader::~TraceUploader() {
49 DCHECK_CURRENTLY_ON(BrowserThread::UI);
52 void TraceUploader::OnURLFetchComplete(const net::URLFetcher* source) {
53 DCHECK_CURRENTLY_ON(BrowserThread::UI);
54 DCHECK_EQ(source, url_fetcher_.get());
55 int response_code = source->GetResponseCode();
58 bool success = (response_code == kHttpResponseOk);
60 source->GetResponseAsString(&report_id);
62 error_message = "Uploading failed, response code: " +
63 base::IntToString(response_code);
66 BrowserThread::PostTask(
67 content::BrowserThread::UI,
69 base::Bind(done_callback_, success, report_id, error_message));
73 void TraceUploader::OnURLFetchUploadProgress(
74 const net::URLFetcher* source, int64 current, int64 total) {
75 DCHECK(url_fetcher_.get());
77 LOG(WARNING) << "Upload progress: " << current << " of " << total;
78 BrowserThread::PostTask(
79 content::BrowserThread::UI,
81 base::Bind(progress_callback_, current, total));
84 void TraceUploader::DoUpload(
85 const std::string& file_contents,
86 UploadProgressCallback progress_callback,
87 UploadDoneCallback done_callback) {
88 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
89 DCHECK(!url_fetcher_.get());
91 progress_callback_ = progress_callback;
92 done_callback_ = done_callback;
94 if (url_fetcher_.get()) {
95 OnUploadError("Already uploading.");
98 scoped_ptr<char[]> compressed_contents(new char[kMaxUploadBytes]);
100 if (!Compress(file_contents, kMaxUploadBytes, compressed_contents.get(),
101 &compressed_bytes)) {
102 OnUploadError("Compressing file failed.");
106 std::string post_data;
107 SetupMultipart("trace.json.gz",
108 std::string(compressed_contents.get(), compressed_bytes),
111 content::BrowserThread::PostTask(
112 content::BrowserThread::UI, FROM_HERE,
113 base::Bind(&TraceUploader::CreateAndStartURLFetcher,
114 base::Unretained(this),
118 void TraceUploader::OnUploadError(std::string error_message) {
119 LOG(ERROR) << error_message;
120 content::BrowserThread::PostTask(
121 content::BrowserThread::UI,
123 base::Bind(done_callback_, false, "", error_message));
126 void TraceUploader::SetupMultipart(const std::string& trace_filename,
127 const std::string& trace_contents,
128 std::string* post_data) {
129 net::AddMultipartValueForUpload("prod", product_, kMultipartBoundary, "",
131 net::AddMultipartValueForUpload("ver", version_ + "-trace",
132 kMultipartBoundary, "", post_data);
133 net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary,
135 net::AddMultipartValueForUpload("type", "trace", kMultipartBoundary,
137 // No minidump means no need for crash to process the report.
138 net::AddMultipartValueForUpload("should_process", "false", kMultipartBoundary,
141 AddTraceFile(trace_filename, trace_contents, post_data);
143 net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data);
146 void TraceUploader::AddTraceFile(const std::string& trace_filename,
147 const std::string& trace_contents,
148 std::string* post_data) {
149 post_data->append("--");
150 post_data->append(kMultipartBoundary);
151 post_data->append("\r\n");
152 post_data->append("Content-Disposition: form-data; name=\"trace\"");
153 post_data->append("; filename=\"");
154 post_data->append(trace_filename);
155 post_data->append("\"\r\n");
156 post_data->append("Content-Type: application/gzip\r\n\r\n");
157 post_data->append(trace_contents);
158 post_data->append("\r\n");
161 bool TraceUploader::Compress(std::string input,
162 int max_compressed_bytes,
164 int* compressed_bytes) {
166 DCHECK(compressed_bytes);
167 z_stream stream = {0};
168 int result = deflateInit2(&stream,
169 Z_DEFAULT_COMPRESSION,
171 // 16 is added to produce a gzip header + trailer.
173 8, // memLevel = 8 is default.
175 DCHECK_EQ(Z_OK, result);
176 stream.next_in = reinterpret_cast<uint8*>(&input[0]);
177 stream.avail_in = input.size();
178 stream.next_out = reinterpret_cast<uint8*>(compressed);
179 stream.avail_out = max_compressed_bytes;
180 // Do a one-shot compression. This will return Z_STREAM_END only if |output|
181 // is large enough to hold all compressed data.
182 result = deflate(&stream, Z_FINISH);
183 bool success = (result == Z_STREAM_END);
184 result = deflateEnd(&stream);
185 DCHECK(result == Z_OK || result == Z_DATA_ERROR);
188 *compressed_bytes = max_compressed_bytes - stream.avail_out;
190 LOG(WARNING) << "input size: " << input.size()
191 << ", output size: " << *compressed_bytes;
195 void TraceUploader::CreateAndStartURLFetcher(const std::string& post_data) {
196 DCHECK_CURRENTLY_ON(BrowserThread::UI);
197 DCHECK(!url_fetcher_.get());
199 std::string content_type = kUploadContentType;
200 content_type.append("; boundary=");
201 content_type.append(kMultipartBoundary);
204 net::URLFetcher::Create(GURL(upload_url_), net::URLFetcher::POST, this));
205 url_fetcher_->SetRequestContext(request_context_);
206 url_fetcher_->SetUploadData(content_type, post_data);
207 url_fetcher_->Start();
210 } // namespace content