1 // Copyright 2013 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/media/webrtc_log_uploader.h"
7 #include "base/file_util.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/memory/shared_memory.h"
11 #include "base/path_service.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/media/webrtc_log_upload_list.h"
17 #include "chrome/common/chrome_paths.h"
18 #include "chrome/common/chrome_version_info.h"
19 #include "chrome/common/partial_circular_buffer.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "net/base/mime_util.h"
22 #include "net/base/network_delegate.h"
23 #include "net/proxy/proxy_config.h"
24 #include "net/proxy/proxy_config_service.h"
25 #include "net/url_request/url_fetcher.h"
26 #include "net/url_request/url_request_context.h"
27 #include "net/url_request/url_request_context_builder.h"
28 #include "net/url_request/url_request_context_getter.h"
29 #include "third_party/zlib/zlib.h"
33 const int kLogCountLimit = 5;
34 const uint32 kIntermediateCompressionBufferBytes = 256 * 1024; // 256 KB
35 const int kLogListLimitLines = 50;
37 const char kUploadURL[] = "https://clients2.google.com/cr/report";
38 const char kUploadContentType[] = "multipart/form-data";
39 const char kMultipartBoundary[] =
40 "----**--yradnuoBgoLtrapitluMklaTelgooG--**----";
42 const int kHttpResponseOk = 200;
46 WebRtcLogUploader::WebRtcLogUploader()
50 WebRtcLogUploader::~WebRtcLogUploader() {}
52 void WebRtcLogUploader::OnURLFetchComplete(
53 const net::URLFetcher* source) {
54 DCHECK(upload_done_data_.find(source) != upload_done_data_.end());
55 int response_code = source->GetResponseCode();
56 std::string report_id;
57 if (response_code == kHttpResponseOk &&
58 source->GetResponseAsString(&report_id)) {
59 AddUploadedLogInfoToUploadListFile(
60 WebRtcLogUploadList::GetFilePathForProfile(
61 upload_done_data_[source].profile),
64 NotifyUploadDone(response_code, report_id, upload_done_data_[source]);
65 upload_done_data_.erase(source);
68 void WebRtcLogUploader::OnURLFetchUploadProgress(
69 const net::URLFetcher* source, int64 current, int64 total) {
72 bool WebRtcLogUploader::ApplyForStartLogging() {
73 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
74 if (log_count_ < kLogCountLimit) {
81 void WebRtcLogUploader::LoggingStoppedDontUpload() {
82 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
83 base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
86 void WebRtcLogUploader::LoggingStoppedDoUpload(
87 net::URLRequestContextGetter* request_context,
88 scoped_ptr<base::SharedMemory> shared_memory,
90 const std::map<std::string, std::string>& meta_data,
91 const WebRtcLogUploadDoneData& upload_done_data) {
92 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
93 DCHECK(shared_memory);
94 DCHECK(shared_memory->memory());
96 std::string post_data;
97 SetupMultipart(&post_data, reinterpret_cast<uint8*>(shared_memory->memory()),
100 // If a test has set the test string pointer, write to it and skip uploading.
101 // Still fire the upload callback so that we can run an extension API test
102 // using the test framework for that without hanging.
103 // TODO(grunell): Remove this when the api test for this feature is fully
104 // implemented according to the test plan. http://crbug.com/257329.
106 *post_data_ = post_data;
107 NotifyUploadDone(kHttpResponseOk, "", upload_done_data);
111 std::string content_type = kUploadContentType;
112 content_type.append("; boundary=");
113 content_type.append(kMultipartBoundary);
115 net::URLFetcher* url_fetcher =
116 net::URLFetcher::Create(GURL(kUploadURL), net::URLFetcher::POST, this);
117 url_fetcher->SetRequestContext(request_context);
118 url_fetcher->SetUploadData(content_type, post_data);
119 url_fetcher->Start();
120 upload_done_data_[url_fetcher] = upload_done_data;
122 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
123 base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
126 void WebRtcLogUploader::SetupMultipart(
127 std::string* post_data, uint8* log_buffer, uint32 log_buffer_length,
128 const std::map<std::string, std::string>& meta_data) {
130 const char product[] = "Chrome";
131 #elif defined(OS_MACOSX)
132 const char product[] = "Chrome_Mac";
133 #elif defined(OS_LINUX)
134 #if !defined(ADDRESS_SANITIZER)
135 const char product[] = "Chrome_Linux";
137 const char product[] = "Chrome_Linux_ASan";
139 #elif defined(OS_ANDROID)
140 const char product[] = "Chrome_Android";
141 #elif defined(OS_CHROMEOS)
142 const char product[] = "Chrome_ChromeOS";
144 // This file should not be compiled for other platforms.
145 COMPILE_ASSERT(false);
147 net::AddMultipartValueForUpload("prod", product, kMultipartBoundary,
149 chrome::VersionInfo version_info;
150 net::AddMultipartValueForUpload("ver", version_info.Version(),
151 kMultipartBoundary, "", post_data);
152 net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary,
154 net::AddMultipartValueForUpload("type", "webrtc_log", kMultipartBoundary,
157 // Add custom meta data.
158 std::map<std::string, std::string>::const_iterator it = meta_data.begin();
159 for (; it != meta_data.end(); ++it) {
160 net::AddMultipartValueForUpload(it->first, it->second, kMultipartBoundary,
164 AddLogData(post_data, log_buffer, log_buffer_length);
165 net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data);
168 void WebRtcLogUploader::AddLogData(std::string* post_data,
170 uint32 log_buffer_length) {
171 post_data->append("--");
172 post_data->append(kMultipartBoundary);
173 post_data->append("\r\n");
174 post_data->append("Content-Disposition: form-data; name=\"webrtc_log\"");
175 post_data->append("; filename=\"webrtc_log.gz\"\r\n");
176 post_data->append("Content-Type: application/gzip\r\n\r\n");
178 CompressLog(post_data, log_buffer, log_buffer_length);
180 post_data->append("\r\n");
183 void WebRtcLogUploader::CompressLog(std::string* post_data,
186 PartialCircularBuffer read_pcb(input, input_size);
188 z_stream stream = {0};
189 int result = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
190 // windowBits = 15 is default, 16 is added to
191 // produce a gzip header + trailer.
193 8, // memLevel = 8 is default.
195 DCHECK_EQ(Z_OK, result);
197 uint8 intermediate_buffer[kIntermediateCompressionBufferBytes] = {0};
198 ResizeForNextOutput(post_data, &stream);
202 if (stream.avail_in == 0) {
203 read = read_pcb.Read(&intermediate_buffer[0],
204 kIntermediateCompressionBufferBytes);
205 stream.next_in = &intermediate_buffer[0];
206 stream.avail_in = read;
207 if (read != kIntermediateCompressionBufferBytes)
210 result = deflate(&stream, Z_SYNC_FLUSH);
211 DCHECK_EQ(Z_OK, result);
212 if (stream.avail_out == 0)
213 ResizeForNextOutput(post_data, &stream);
216 // Ensure we have enough room in the output buffer. Easier to always just do a
217 // resize than looping around and resize if needed.
218 if (stream.avail_out < kIntermediateCompressionBufferBytes)
219 ResizeForNextOutput(post_data, &stream);
221 result = deflate(&stream, Z_FINISH);
222 DCHECK_EQ(Z_STREAM_END, result);
223 result = deflateEnd(&stream);
224 DCHECK_EQ(Z_OK, result);
226 post_data->resize(post_data->size() - stream.avail_out);
229 void WebRtcLogUploader::ResizeForNextOutput(std::string* post_data,
231 size_t old_size = post_data->size() - stream->avail_out;
232 post_data->resize(old_size + kIntermediateCompressionBufferBytes);
233 stream->next_out = reinterpret_cast<uint8*>(&(*post_data)[old_size]);
234 stream->avail_out = kIntermediateCompressionBufferBytes;
237 void WebRtcLogUploader::DecreaseLogCount() {
238 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
242 void WebRtcLogUploader::AddUploadedLogInfoToUploadListFile(
243 const base::FilePath& upload_list_path,
244 const std::string& report_id) {
245 std::string contents;
247 if (base::PathExists(upload_list_path)) {
248 bool read_ok = base::ReadFileToString(upload_list_path, &contents);
251 // Limit the number of log entries to |kLogListLimitLines| - 1, to make room
252 // for the new entry. Each line including the last ends with a '\n', so hit
253 // n will be before line n-1 (from the back).
255 int i = contents.size() - 1;
256 for (; i >= 0 && lf_count < kLogListLimitLines; --i) {
257 if (contents[i] == '\n')
260 if (lf_count >= kLogListLimitLines) {
261 // + 1 to compensate for the for loop decrease before the conditional
262 // check and + 1 to get the length.
263 contents.erase(0, i + 2);
267 // Write the Unix time and report ID to the log list file.
268 base::Time time_now = base::Time::Now();
269 contents += base::DoubleToString(time_now.ToDoubleT()) +
270 "," + report_id + '\n';
272 int written = file_util::WriteFile(upload_list_path, &contents[0],
274 DPCHECK(written == static_cast<int>(contents.size()));
277 void WebRtcLogUploader::NotifyUploadDone(
279 const std::string& report_id,
280 const WebRtcLogUploadDoneData& upload_done_data) {
281 content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
282 base::Bind(&WebRtcLoggingHandlerHost::UploadLogDone,
283 upload_done_data.host));
284 if (!upload_done_data.callback.is_null()) {
285 bool success = response_code == kHttpResponseOk;
286 std::string error_message;
288 error_message = "Uploading failed, response code: " +
289 base::IntToString(response_code);
291 content::BrowserThread::PostTask(
292 content::BrowserThread::UI, FROM_HERE,
293 base::Bind(upload_done_data.callback, success, report_id,