- add sources.
[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/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"
30
31 namespace {
32
33 const int kLogCountLimit = 5;
34 const uint32 kIntermediateCompressionBufferBytes = 256 * 1024;  // 256 KB
35 const int kLogListLimitLines = 50;
36
37 const char kUploadURL[] = "https://clients2.google.com/cr/report";
38 const char kUploadContentType[] = "multipart/form-data";
39 const char kMultipartBoundary[] =
40     "----**--yradnuoBgoLtrapitluMklaTelgooG--**----";
41
42 const int kHttpResponseOk = 200;
43
44 }  // namespace
45
46 WebRtcLogUploader::WebRtcLogUploader()
47     : log_count_(0),
48       post_data_(NULL) {}
49
50 WebRtcLogUploader::~WebRtcLogUploader() {}
51
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),
62         report_id);
63   }
64   NotifyUploadDone(response_code, report_id, upload_done_data_[source]);
65   upload_done_data_.erase(source);
66 }
67
68 void WebRtcLogUploader::OnURLFetchUploadProgress(
69     const net::URLFetcher* source, int64 current, int64 total) {
70 }
71
72 bool WebRtcLogUploader::ApplyForStartLogging() {
73   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
74   if (log_count_ < kLogCountLimit) {
75     ++log_count_;
76     return true;
77   }
78   return false;
79 }
80
81 void WebRtcLogUploader::LoggingStoppedDontUpload() {
82   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
83       base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
84 }
85
86 void WebRtcLogUploader::LoggingStoppedDoUpload(
87     net::URLRequestContextGetter* request_context,
88     scoped_ptr<base::SharedMemory> shared_memory,
89     uint32 length,
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());
95
96   std::string post_data;
97   SetupMultipart(&post_data, reinterpret_cast<uint8*>(shared_memory->memory()),
98                  length, meta_data);
99
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.
105   if (post_data_) {
106     *post_data_ = post_data;
107     NotifyUploadDone(kHttpResponseOk, "", upload_done_data);
108     return;
109   }
110
111   std::string content_type = kUploadContentType;
112   content_type.append("; boundary=");
113   content_type.append(kMultipartBoundary);
114
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;
121
122   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
123       base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
124 }
125
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) {
129 #if defined(OS_WIN)
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";
136 #else
137   const char product[] = "Chrome_Linux_ASan";
138 #endif
139 #elif defined(OS_ANDROID)
140   const char product[] = "Chrome_Android";
141 #elif defined(OS_CHROMEOS)
142   const char product[] = "Chrome_ChromeOS";
143 #else
144   // This file should not be compiled for other platforms.
145   COMPILE_ASSERT(false);
146 #endif
147   net::AddMultipartValueForUpload("prod", product, kMultipartBoundary,
148                                   "", post_data);
149   chrome::VersionInfo version_info;
150   net::AddMultipartValueForUpload("ver", version_info.Version(),
151                                   kMultipartBoundary, "", post_data);
152   net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary,
153                                   "", post_data);
154   net::AddMultipartValueForUpload("type", "webrtc_log", kMultipartBoundary,
155                                   "", post_data);
156
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,
161                                     "", post_data);
162   }
163
164   AddLogData(post_data, log_buffer, log_buffer_length);
165   net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data);
166 }
167
168 void WebRtcLogUploader::AddLogData(std::string* post_data,
169                                    uint8* log_buffer,
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");
177
178   CompressLog(post_data, log_buffer, log_buffer_length);
179
180   post_data->append("\r\n");
181 }
182
183 void WebRtcLogUploader::CompressLog(std::string* post_data,
184                                     uint8* input,
185                                     uint32 input_size) {
186   PartialCircularBuffer read_pcb(input, input_size);
187
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.
192                             15 + 16,
193                             8,  // memLevel = 8 is default.
194                             Z_DEFAULT_STRATEGY);
195   DCHECK_EQ(Z_OK, result);
196
197   uint8 intermediate_buffer[kIntermediateCompressionBufferBytes] = {0};
198   ResizeForNextOutput(post_data, &stream);
199   uint32 read = 0;
200
201   do {
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)
208         break;
209     }
210     result = deflate(&stream, Z_SYNC_FLUSH);
211     DCHECK_EQ(Z_OK, result);
212     if (stream.avail_out == 0)
213       ResizeForNextOutput(post_data, &stream);
214   } while (true);
215
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);
220
221   result = deflate(&stream, Z_FINISH);
222   DCHECK_EQ(Z_STREAM_END, result);
223   result = deflateEnd(&stream);
224   DCHECK_EQ(Z_OK, result);
225
226   post_data->resize(post_data->size() - stream.avail_out);
227 }
228
229 void WebRtcLogUploader::ResizeForNextOutput(std::string* post_data,
230                                             z_stream* stream) {
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;
235 }
236
237 void WebRtcLogUploader::DecreaseLogCount() {
238   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
239   --log_count_;
240 }
241
242 void WebRtcLogUploader::AddUploadedLogInfoToUploadListFile(
243     const base::FilePath& upload_list_path,
244     const std::string& report_id) {
245   std::string contents;
246
247   if (base::PathExists(upload_list_path)) {
248     bool read_ok = base::ReadFileToString(upload_list_path, &contents);
249     DPCHECK(read_ok);
250
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).
254     int lf_count = 0;
255     int i = contents.size() - 1;
256     for (; i >= 0 && lf_count < kLogListLimitLines; --i) {
257       if (contents[i] == '\n')
258         ++lf_count;
259     }
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);
264     }
265   }
266
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';
271
272   int written = file_util::WriteFile(upload_list_path, &contents[0],
273                                      contents.size());
274   DPCHECK(written == static_cast<int>(contents.size()));
275 }
276
277 void WebRtcLogUploader::NotifyUploadDone(
278     int response_code,
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;
287     if (!success) {
288       error_message = "Uploading failed, response code: " +
289                       base::IntToString(response_code);
290     }
291     content::BrowserThread::PostTask(
292         content::BrowserThread::UI, FROM_HERE,
293         base::Bind(upload_done_data.callback, success, report_id,
294                    error_message));
295   }
296 }