Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / media / webrtc_log_uploader.cc
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.
4
5 #include "chrome/browser/media/webrtc_log_uploader.h"
6
7 #include "base/files/file_enumerator.h"
8 #include "base/files/file_path.h"
9 #include "base/files/file_util.h"
10 #include "base/logging.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/media/webrtc_log_list.h"
17 #include "chrome/browser/media/webrtc_log_util.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/url_request/url_fetcher.h"
23 #include "third_party/zlib/zlib.h"
24
25 namespace {
26
27 const int kLogCountLimit = 5;
28 const uint32 kIntermediateCompressionBufferBytes = 256 * 1024;  // 256 KB
29 const int kLogListLimitLines = 50;
30
31 const char kUploadURL[] = "https://clients2.google.com/cr/report";
32 const char kUploadContentType[] = "multipart/form-data";
33 const char kMultipartBoundary[] =
34     "----**--yradnuoBgoLtrapitluMklaTelgooG--**----";
35
36 const int kHttpResponseOk = 200;
37
38 // Adds the header section for a gzip file to the multipart |post_data|.
39 void AddMultipartFileContentHeader(std::string* post_data,
40                                    const std::string& content_name) {
41   post_data->append("--");
42   post_data->append(kMultipartBoundary);
43   post_data->append("\r\nContent-Disposition: form-data; name=\"");
44   post_data->append(content_name);
45   post_data->append("\"; filename=\"");
46   post_data->append(content_name + ".gz");
47   post_data->append("\"\r\nContent-Type: application/gzip\r\n\r\n");
48 }
49
50 // Adds |compressed_log| to |post_data|.
51 void AddLogData(std::string* post_data,
52                 const std::vector<uint8>& compressed_log) {
53   AddMultipartFileContentHeader(post_data, "webrtc_log");
54   post_data->append(reinterpret_cast<const char*>(&compressed_log[0]),
55                     compressed_log.size());
56   post_data->append("\r\n");
57 }
58
59 // Adds the RTP dump data to |post_data|.
60 void AddRtpDumpData(std::string* post_data,
61                     const std::string& name,
62                     const std::string& dump_data) {
63   AddMultipartFileContentHeader(post_data, name);
64   post_data->append(dump_data.data(), dump_data.size());
65   post_data->append("\r\n");
66 }
67
68 }  // namespace
69
70 WebRtcLogUploadDoneData::WebRtcLogUploadDoneData() {}
71
72 WebRtcLogUploadDoneData::~WebRtcLogUploadDoneData() {}
73
74 WebRtcLogUploader::WebRtcLogUploader()
75     : log_count_(0),
76       post_data_(NULL),
77       shutting_down_(false) {
78   file_thread_checker_.DetachFromThread();
79 }
80
81 WebRtcLogUploader::~WebRtcLogUploader() {
82   DCHECK(create_thread_checker_.CalledOnValidThread());
83   DCHECK(upload_done_data_.empty());
84   DCHECK(shutting_down_);
85 }
86
87 void WebRtcLogUploader::OnURLFetchComplete(
88     const net::URLFetcher* source) {
89   DCHECK(create_thread_checker_.CalledOnValidThread());
90   DCHECK(upload_done_data_.find(source) != upload_done_data_.end());
91   DCHECK(!shutting_down_);
92   int response_code = source->GetResponseCode();
93   UploadDoneDataMap::iterator it = upload_done_data_.find(source);
94   if (it != upload_done_data_.end()) {
95     // The log path can be empty here if we failed getting it before. We still
96     // upload the log if that's the case.
97     std::string report_id;
98     if (response_code == kHttpResponseOk &&
99         source->GetResponseAsString(&report_id) &&
100         !it->second.log_path.empty()) {
101       // TODO(jiayl): Add the RTP dump records to chrome://webrtc-logs.
102       base::FilePath log_list_path =
103           WebRtcLogList::GetWebRtcLogListFileForDirectory(it->second.log_path);
104       content::BrowserThread::PostTask(
105           content::BrowserThread::FILE,
106           FROM_HERE,
107           base::Bind(&WebRtcLogUploader::AddUploadedLogInfoToUploadListFile,
108                      base::Unretained(this),
109                      log_list_path,
110                      it->second.local_log_id,
111                      report_id));
112     }
113     NotifyUploadDone(response_code, report_id, it->second);
114     upload_done_data_.erase(it);
115   }
116
117   delete source;
118 }
119
120 void WebRtcLogUploader::OnURLFetchUploadProgress(
121     const net::URLFetcher* source, int64 current, int64 total) {
122 }
123
124 bool WebRtcLogUploader::ApplyForStartLogging() {
125   DCHECK(create_thread_checker_.CalledOnValidThread());
126   if (log_count_ < kLogCountLimit && !shutting_down_) {
127     ++log_count_;
128     return true;
129   }
130   return false;
131 }
132
133 void WebRtcLogUploader::LoggingStoppedDontUpload() {
134   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
135       base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
136 }
137
138 void WebRtcLogUploader::LoggingStoppedDoUpload(
139     scoped_ptr<unsigned char[]> log_buffer,
140     uint32 length,
141     const std::map<std::string, std::string>& meta_data,
142     const WebRtcLogUploadDoneData& upload_done_data) {
143   DCHECK(file_thread_checker_.CalledOnValidThread());
144   DCHECK(log_buffer.get());
145   DCHECK(!upload_done_data.log_path.empty());
146
147   std::vector<uint8> compressed_log;
148   CompressLog(
149       &compressed_log, reinterpret_cast<uint8*>(&log_buffer[0]), length);
150
151   std::string local_log_id;
152
153   if (base::PathExists(upload_done_data.log_path)) {
154     WebRtcLogUtil::DeleteOldWebRtcLogFiles(upload_done_data.log_path);
155
156     local_log_id = base::DoubleToString(base::Time::Now().ToDoubleT());
157     base::FilePath log_file_path =
158         upload_done_data.log_path.AppendASCII(local_log_id)
159             .AddExtension(FILE_PATH_LITERAL(".gz"));
160     WriteCompressedLogToFile(compressed_log, log_file_path);
161
162     base::FilePath log_list_path =
163         WebRtcLogList::GetWebRtcLogListFileForDirectory(
164             upload_done_data.log_path);
165     AddLocallyStoredLogInfoToUploadListFile(log_list_path, local_log_id);
166   }
167
168   WebRtcLogUploadDoneData upload_done_data_with_log_id = upload_done_data;
169   upload_done_data_with_log_id.local_log_id = local_log_id;
170
171   scoped_ptr<std::string> post_data(new std::string());
172   SetupMultipart(post_data.get(),
173                  compressed_log,
174                  upload_done_data.incoming_rtp_dump,
175                  upload_done_data.outgoing_rtp_dump,
176                  meta_data);
177
178   // If a test has set the test string pointer, write to it and skip uploading.
179   // Still fire the upload callback so that we can run an extension API test
180   // using the test framework for that without hanging.
181   // TODO(grunell): Remove this when the api test for this feature is fully
182   // implemented according to the test plan. http://crbug.com/257329.
183   if (post_data_) {
184     *post_data_ = *post_data;
185     NotifyUploadDone(kHttpResponseOk, "", upload_done_data_with_log_id);
186     return;
187   }
188
189   content::BrowserThread::PostTask(
190       content::BrowserThread::UI,
191       FROM_HERE,
192       base::Bind(&WebRtcLogUploader::CreateAndStartURLFetcher,
193                  base::Unretained(this),
194                  upload_done_data_with_log_id,
195                  Passed(&post_data)));
196
197   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
198       base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
199 }
200
201 void WebRtcLogUploader::StartShutdown() {
202   DCHECK(create_thread_checker_.CalledOnValidThread());
203   DCHECK(!shutting_down_);
204
205   // Delete all URLFetchers first and clear the upload done map.
206   for (UploadDoneDataMap::iterator it = upload_done_data_.begin();
207        it != upload_done_data_.end();
208        ++it) {
209     delete it->first;
210   }
211   upload_done_data_.clear();
212   shutting_down_ = true;
213 }
214
215 void WebRtcLogUploader::SetupMultipart(
216     std::string* post_data,
217     const std::vector<uint8>& compressed_log,
218     const base::FilePath& incoming_rtp_dump,
219     const base::FilePath& outgoing_rtp_dump,
220     const std::map<std::string, std::string>& meta_data) {
221 #if defined(OS_WIN)
222   const char product[] = "Chrome";
223 #elif defined(OS_MACOSX)
224   const char product[] = "Chrome_Mac";
225 #elif defined(OS_LINUX)
226 #if !defined(ADDRESS_SANITIZER)
227   const char product[] = "Chrome_Linux";
228 #else
229   const char product[] = "Chrome_Linux_ASan";
230 #endif
231 #elif defined(OS_ANDROID)
232   const char product[] = "Chrome_Android";
233 #elif defined(OS_CHROMEOS)
234   const char product[] = "Chrome_ChromeOS";
235 #else
236 #error Platform not supported.
237 #endif
238   net::AddMultipartValueForUpload("prod", product, kMultipartBoundary,
239                                   "", post_data);
240   chrome::VersionInfo version_info;
241   net::AddMultipartValueForUpload("ver", version_info.Version() + "-webrtc",
242                                   kMultipartBoundary, "", post_data);
243   net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary,
244                                   "", post_data);
245   net::AddMultipartValueForUpload("type", "webrtc_log", kMultipartBoundary,
246                                   "", post_data);
247
248   // Add custom meta data.
249   std::map<std::string, std::string>::const_iterator it = meta_data.begin();
250   for (; it != meta_data.end(); ++it) {
251     net::AddMultipartValueForUpload(it->first, it->second, kMultipartBoundary,
252                                     "", post_data);
253   }
254
255   AddLogData(post_data, compressed_log);
256
257   // Add the rtp dumps if they exist.
258   base::FilePath rtp_dumps[2] = {incoming_rtp_dump, outgoing_rtp_dump};
259   static const char* kRtpDumpNames[2] = {"rtpdump_recv", "rtpdump_send"};
260
261   for (size_t i = 0; i < 2; ++i) {
262     if (!rtp_dumps[i].empty() && base::PathExists(rtp_dumps[i])) {
263       std::string dump_data;
264       if (base::ReadFileToString(rtp_dumps[i], &dump_data))
265         AddRtpDumpData(post_data, kRtpDumpNames[i], dump_data);
266     }
267   }
268
269   net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data);
270 }
271
272 void WebRtcLogUploader::CompressLog(std::vector<uint8>* compressed_log,
273                                     uint8* input,
274                                     uint32 input_size) {
275   PartialCircularBuffer read_pcb(input, input_size);
276
277   z_stream stream = {0};
278   int result = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
279                             // windowBits = 15 is default, 16 is added to
280                             // produce a gzip header + trailer.
281                             15 + 16,
282                             8,  // memLevel = 8 is default.
283                             Z_DEFAULT_STRATEGY);
284   DCHECK_EQ(Z_OK, result);
285
286   uint8 intermediate_buffer[kIntermediateCompressionBufferBytes] = {0};
287   ResizeForNextOutput(compressed_log, &stream);
288   uint32 read = 0;
289
290   do {
291     if (stream.avail_in == 0) {
292       read = read_pcb.Read(&intermediate_buffer[0],
293                            kIntermediateCompressionBufferBytes);
294       stream.next_in = &intermediate_buffer[0];
295       stream.avail_in = read;
296       if (read != kIntermediateCompressionBufferBytes)
297         break;
298     }
299     result = deflate(&stream, Z_SYNC_FLUSH);
300     DCHECK_EQ(Z_OK, result);
301     if (stream.avail_out == 0)
302       ResizeForNextOutput(compressed_log, &stream);
303   } while (true);
304
305   // Ensure we have enough room in the output buffer. Easier to always just do a
306   // resize than looping around and resize if needed.
307   if (stream.avail_out < kIntermediateCompressionBufferBytes)
308     ResizeForNextOutput(compressed_log, &stream);
309
310   result = deflate(&stream, Z_FINISH);
311   DCHECK_EQ(Z_STREAM_END, result);
312   result = deflateEnd(&stream);
313   DCHECK_EQ(Z_OK, result);
314
315   compressed_log->resize(compressed_log->size() - stream.avail_out);
316 }
317
318 void WebRtcLogUploader::ResizeForNextOutput(std::vector<uint8>* compressed_log,
319                                             z_stream* stream) {
320   size_t old_size = compressed_log->size() - stream->avail_out;
321   compressed_log->resize(old_size + kIntermediateCompressionBufferBytes);
322   stream->next_out = &(*compressed_log)[old_size];
323   stream->avail_out = kIntermediateCompressionBufferBytes;
324 }
325
326 void WebRtcLogUploader::CreateAndStartURLFetcher(
327     const WebRtcLogUploadDoneData& upload_done_data,
328     scoped_ptr<std::string> post_data) {
329   DCHECK(create_thread_checker_.CalledOnValidThread());
330
331   if (shutting_down_)
332     return;
333
334   std::string content_type = kUploadContentType;
335   content_type.append("; boundary=");
336   content_type.append(kMultipartBoundary);
337
338   net::URLFetcher* url_fetcher =
339       net::URLFetcher::Create(GURL(kUploadURL), net::URLFetcher::POST, this);
340   url_fetcher->SetRequestContext(g_browser_process->system_request_context());
341   url_fetcher->SetUploadData(content_type, *post_data);
342   url_fetcher->Start();
343   upload_done_data_[url_fetcher] = upload_done_data;
344 }
345
346 void WebRtcLogUploader::DecreaseLogCount() {
347   DCHECK(create_thread_checker_.CalledOnValidThread());
348   --log_count_;
349 }
350
351 void WebRtcLogUploader::WriteCompressedLogToFile(
352     const std::vector<uint8>& compressed_log,
353     const base::FilePath& log_file_path) {
354   DCHECK(file_thread_checker_.CalledOnValidThread());
355   DCHECK(!compressed_log.empty());
356   base::WriteFile(log_file_path,
357                   reinterpret_cast<const char*>(&compressed_log[0]),
358                   compressed_log.size());
359 }
360
361 void WebRtcLogUploader::AddLocallyStoredLogInfoToUploadListFile(
362     const base::FilePath& upload_list_path,
363     const std::string& local_log_id) {
364   DCHECK(file_thread_checker_.CalledOnValidThread());
365   DCHECK(!upload_list_path.empty());
366   DCHECK(!local_log_id.empty());
367
368   std::string contents;
369
370   if (base::PathExists(upload_list_path)) {
371     if (!base::ReadFileToString(upload_list_path, &contents)) {
372       DPLOG(WARNING) << "Could not read WebRTC log list file.";
373       return;
374     }
375
376     // Limit the number of log entries to |kLogListLimitLines| - 1, to make room
377     // for the new entry. Each line including the last ends with a '\n', so hit
378     // n will be before line n-1 (from the back).
379     int lf_count = 0;
380     int i = contents.size() - 1;
381     for (; i >= 0 && lf_count < kLogListLimitLines; --i) {
382       if (contents[i] == '\n')
383         ++lf_count;
384     }
385     if (lf_count >= kLogListLimitLines) {
386       // + 1 to compensate for the for loop decrease before the conditional
387       // check and + 1 to get the length.
388       contents.erase(0, i + 2);
389     }
390   }
391
392   // Write the log ID to the log list file. Leave the upload time and report ID
393   // empty.
394   contents += ",," + local_log_id + '\n';
395
396   int written =
397       base::WriteFile(upload_list_path, &contents[0], contents.size());
398   if (written != static_cast<int>(contents.size())) {
399     DPLOG(WARNING) << "Could not write all data to WebRTC log list file: "
400                    << written;
401   }
402 }
403
404 void WebRtcLogUploader::AddUploadedLogInfoToUploadListFile(
405     const base::FilePath& upload_list_path,
406     const std::string& local_log_id,
407     const std::string& report_id) {
408   DCHECK(file_thread_checker_.CalledOnValidThread());
409   DCHECK(!upload_list_path.empty());
410   DCHECK(!local_log_id.empty());
411   DCHECK(!report_id.empty());
412
413   std::string contents;
414
415   if (base::PathExists(upload_list_path)) {
416     if (!base::ReadFileToString(upload_list_path, &contents)) {
417       DPLOG(WARNING) << "Could not read WebRTC log list file.";
418       return;
419     }
420   }
421
422   // Write the Unix time and report ID to the log list file. We should be able
423   // to find the local log ID, in that case insert the data into the existing
424   // line. Otherwise add it in the end.
425   base::Time time_now = base::Time::Now();
426   std::string time_now_str = base::DoubleToString(time_now.ToDoubleT());
427   size_t pos = contents.find(",," + local_log_id);
428   if (pos != std::string::npos) {
429     contents.insert(pos, time_now_str);
430     contents.insert(pos + time_now_str.length() + 1, report_id);
431   } else {
432     contents += time_now_str + "," + report_id + ",\n";
433   }
434
435   int written =
436       base::WriteFile(upload_list_path, &contents[0], contents.size());
437   if (written != static_cast<int>(contents.size())) {
438     DPLOG(WARNING) << "Could not write all data to WebRTC log list file: "
439                    << written;
440   }
441 }
442
443 void WebRtcLogUploader::NotifyUploadDone(
444     int response_code,
445     const std::string& report_id,
446     const WebRtcLogUploadDoneData& upload_done_data) {
447   content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
448       base::Bind(&WebRtcLoggingHandlerHost::UploadLogDone,
449                  upload_done_data.host));
450   if (!upload_done_data.callback.is_null()) {
451     bool success = response_code == kHttpResponseOk;
452     std::string error_message;
453     if (!success) {
454       error_message = "Uploading failed, response code: " +
455                       base::IntToString(response_code);
456     }
457     content::BrowserThread::PostTask(
458         content::BrowserThread::UI, FROM_HERE,
459         base::Bind(upload_done_data.callback, success, report_id,
460                    error_message));
461   }
462 }