Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / media / webrtc_log_uploader.cc
blob8752cb373a9b7b70b4242bc8e9faf7f047f38489
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"
31 namespace {
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;
44 } // namespace
46 WebRtcLogUploader::WebRtcLogUploader()
47 : log_count_(0),
48 post_data_(NULL) {
49 file_thread_checker_.DetachFromThread();
52 WebRtcLogUploader::~WebRtcLogUploader() {
53 DCHECK(create_thread_checker_.CalledOnValidThread());
54 // Delete all pending URLFetcher and release all references to
55 // WebRtcLoggingHandlerHost.
56 for (UploadDoneDataMap::iterator it = upload_done_data_.begin();
57 it != upload_done_data_.end(); ++it) {
58 delete it->first;
60 upload_done_data_.clear();
63 void WebRtcLogUploader::OnURLFetchComplete(
64 const net::URLFetcher* source) {
65 DCHECK(create_thread_checker_.CalledOnValidThread());
66 DCHECK(upload_done_data_.find(source) != upload_done_data_.end());
67 int response_code = source->GetResponseCode();
68 std::string report_id;
69 if (response_code == kHttpResponseOk &&
70 source->GetResponseAsString(&report_id)) {
71 content::BrowserThread::PostTask(
72 content::BrowserThread::FILE, FROM_HERE,
73 base::Bind(&WebRtcLogUploader::AddUploadedLogInfoToUploadListFile,
74 base::Unretained(this),
75 upload_done_data_[source].upload_list_path,
76 report_id));
78 NotifyUploadDone(response_code, report_id, upload_done_data_[source]);
79 upload_done_data_.erase(source);
80 delete source;
83 void WebRtcLogUploader::OnURLFetchUploadProgress(
84 const net::URLFetcher* source, int64 current, int64 total) {
87 bool WebRtcLogUploader::ApplyForStartLogging() {
88 DCHECK(create_thread_checker_.CalledOnValidThread());
89 if (log_count_ < kLogCountLimit) {
90 ++log_count_;
91 return true;
93 return false;
96 void WebRtcLogUploader::LoggingStoppedDontUpload() {
97 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
98 base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
101 void WebRtcLogUploader::LoggingStoppedDoUpload(
102 net::URLRequestContextGetter* request_context,
103 scoped_ptr<unsigned char[]> log_buffer,
104 uint32 length,
105 const std::map<std::string, std::string>& meta_data,
106 const WebRtcLogUploadDoneData& upload_done_data) {
107 DCHECK(file_thread_checker_.CalledOnValidThread());
108 DCHECK(log_buffer.get());
109 DCHECK(!upload_done_data.upload_list_path.empty());
111 scoped_ptr<std::string> post_data;
112 post_data.reset(new std::string);
113 SetupMultipart(post_data.get(),
114 reinterpret_cast<uint8*>(log_buffer.get()),
115 length,
116 meta_data);
118 // If a test has set the test string pointer, write to it and skip uploading.
119 // Still fire the upload callback so that we can run an extension API test
120 // using the test framework for that without hanging.
121 // TODO(grunell): Remove this when the api test for this feature is fully
122 // implemented according to the test plan. http://crbug.com/257329.
123 if (post_data_) {
124 *post_data_ = *post_data;
125 NotifyUploadDone(kHttpResponseOk, "", upload_done_data);
126 return;
129 content::BrowserThread::PostTask(
130 content::BrowserThread::UI, FROM_HERE,
131 base::Bind(&WebRtcLogUploader::CreateAndStartURLFetcher,
132 base::Unretained(this),
133 make_scoped_refptr(request_context),
134 upload_done_data,
135 Passed(&post_data)));
137 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
138 base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
141 void WebRtcLogUploader::SetupMultipart(
142 std::string* post_data, uint8* log_buffer, uint32 log_buffer_length,
143 const std::map<std::string, std::string>& meta_data) {
144 #if defined(OS_WIN)
145 const char product[] = "Chrome";
146 #elif defined(OS_MACOSX)
147 const char product[] = "Chrome_Mac";
148 #elif defined(OS_LINUX)
149 #if !defined(ADDRESS_SANITIZER)
150 const char product[] = "Chrome_Linux";
151 #else
152 const char product[] = "Chrome_Linux_ASan";
153 #endif
154 #elif defined(OS_ANDROID)
155 const char product[] = "Chrome_Android";
156 #elif defined(OS_CHROMEOS)
157 const char product[] = "Chrome_ChromeOS";
158 #else
159 // This file should not be compiled for other platforms.
160 COMPILE_ASSERT(false);
161 #endif
162 net::AddMultipartValueForUpload("prod", product, kMultipartBoundary,
163 "", post_data);
164 chrome::VersionInfo version_info;
165 net::AddMultipartValueForUpload("ver", version_info.Version(),
166 kMultipartBoundary, "", post_data);
167 net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary,
168 "", post_data);
169 net::AddMultipartValueForUpload("type", "webrtc_log", kMultipartBoundary,
170 "", post_data);
172 // Add custom meta data.
173 std::map<std::string, std::string>::const_iterator it = meta_data.begin();
174 for (; it != meta_data.end(); ++it) {
175 net::AddMultipartValueForUpload(it->first, it->second, kMultipartBoundary,
176 "", post_data);
179 AddLogData(post_data, log_buffer, log_buffer_length);
180 net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data);
183 void WebRtcLogUploader::AddLogData(std::string* post_data,
184 uint8* log_buffer,
185 uint32 log_buffer_length) {
186 post_data->append("--");
187 post_data->append(kMultipartBoundary);
188 post_data->append("\r\n");
189 post_data->append("Content-Disposition: form-data; name=\"webrtc_log\"");
190 post_data->append("; filename=\"webrtc_log.gz\"\r\n");
191 post_data->append("Content-Type: application/gzip\r\n\r\n");
193 CompressLog(post_data, log_buffer, log_buffer_length);
195 post_data->append("\r\n");
198 void WebRtcLogUploader::CompressLog(std::string* post_data,
199 uint8* input,
200 uint32 input_size) {
201 PartialCircularBuffer read_pcb(input, input_size);
203 z_stream stream = {0};
204 int result = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
205 // windowBits = 15 is default, 16 is added to
206 // produce a gzip header + trailer.
207 15 + 16,
208 8, // memLevel = 8 is default.
209 Z_DEFAULT_STRATEGY);
210 DCHECK_EQ(Z_OK, result);
212 uint8 intermediate_buffer[kIntermediateCompressionBufferBytes] = {0};
213 ResizeForNextOutput(post_data, &stream);
214 uint32 read = 0;
216 do {
217 if (stream.avail_in == 0) {
218 read = read_pcb.Read(&intermediate_buffer[0],
219 kIntermediateCompressionBufferBytes);
220 stream.next_in = &intermediate_buffer[0];
221 stream.avail_in = read;
222 if (read != kIntermediateCompressionBufferBytes)
223 break;
225 result = deflate(&stream, Z_SYNC_FLUSH);
226 DCHECK_EQ(Z_OK, result);
227 if (stream.avail_out == 0)
228 ResizeForNextOutput(post_data, &stream);
229 } while (true);
231 // Ensure we have enough room in the output buffer. Easier to always just do a
232 // resize than looping around and resize if needed.
233 if (stream.avail_out < kIntermediateCompressionBufferBytes)
234 ResizeForNextOutput(post_data, &stream);
236 result = deflate(&stream, Z_FINISH);
237 DCHECK_EQ(Z_STREAM_END, result);
238 result = deflateEnd(&stream);
239 DCHECK_EQ(Z_OK, result);
241 post_data->resize(post_data->size() - stream.avail_out);
244 void WebRtcLogUploader::ResizeForNextOutput(std::string* post_data,
245 z_stream* stream) {
246 size_t old_size = post_data->size() - stream->avail_out;
247 post_data->resize(old_size + kIntermediateCompressionBufferBytes);
248 stream->next_out = reinterpret_cast<uint8*>(&(*post_data)[old_size]);
249 stream->avail_out = kIntermediateCompressionBufferBytes;
252 void WebRtcLogUploader::CreateAndStartURLFetcher(
253 scoped_refptr<net::URLRequestContextGetter> request_context,
254 const WebRtcLogUploadDoneData& upload_done_data,
255 scoped_ptr<std::string> post_data) {
256 DCHECK(create_thread_checker_.CalledOnValidThread());
257 std::string content_type = kUploadContentType;
258 content_type.append("; boundary=");
259 content_type.append(kMultipartBoundary);
261 net::URLFetcher* url_fetcher =
262 net::URLFetcher::Create(GURL(kUploadURL), net::URLFetcher::POST, this);
263 url_fetcher->SetRequestContext(request_context);
264 url_fetcher->SetUploadData(content_type, *post_data);
265 url_fetcher->Start();
266 upload_done_data_[url_fetcher] = upload_done_data;
269 void WebRtcLogUploader::DecreaseLogCount() {
270 DCHECK(create_thread_checker_.CalledOnValidThread());
271 --log_count_;
274 void WebRtcLogUploader::AddUploadedLogInfoToUploadListFile(
275 const base::FilePath& upload_list_path,
276 const std::string& report_id) {
277 DCHECK(file_thread_checker_.CalledOnValidThread());
278 std::string contents;
280 if (base::PathExists(upload_list_path)) {
281 bool read_ok = base::ReadFileToString(upload_list_path, &contents);
282 DPCHECK(read_ok);
284 // Limit the number of log entries to |kLogListLimitLines| - 1, to make room
285 // for the new entry. Each line including the last ends with a '\n', so hit
286 // n will be before line n-1 (from the back).
287 int lf_count = 0;
288 int i = contents.size() - 1;
289 for (; i >= 0 && lf_count < kLogListLimitLines; --i) {
290 if (contents[i] == '\n')
291 ++lf_count;
293 if (lf_count >= kLogListLimitLines) {
294 // + 1 to compensate for the for loop decrease before the conditional
295 // check and + 1 to get the length.
296 contents.erase(0, i + 2);
300 // Write the Unix time and report ID to the log list file.
301 base::Time time_now = base::Time::Now();
302 contents += base::DoubleToString(time_now.ToDoubleT()) +
303 "," + report_id + '\n';
305 int written = file_util::WriteFile(upload_list_path, &contents[0],
306 contents.size());
307 DPCHECK(written == static_cast<int>(contents.size()));
310 void WebRtcLogUploader::NotifyUploadDone(
311 int response_code,
312 const std::string& report_id,
313 const WebRtcLogUploadDoneData& upload_done_data) {
314 content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
315 base::Bind(&WebRtcLoggingHandlerHost::UploadLogDone,
316 upload_done_data.host));
317 if (!upload_done_data.callback.is_null()) {
318 bool success = response_code == kHttpResponseOk;
319 std::string error_message;
320 if (!success) {
321 error_message = "Uploading failed, response code: " +
322 base::IntToString(response_code);
324 content::BrowserThread::PostTask(
325 content::BrowserThread::UI, FROM_HERE,
326 base::Bind(upload_done_data.callback, success, report_id,
327 error_message));