Home | History | Annotate | Download | only in media
      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   file_thread_checker_.DetachFromThread();
     50 }
     51 
     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;
     59   }
     60   upload_done_data_.clear();
     61 }
     62 
     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));
     77   }
     78   NotifyUploadDone(response_code, report_id, upload_done_data_[source]);
     79   upload_done_data_.erase(source);
     80   delete source;
     81 }
     82 
     83 void WebRtcLogUploader::OnURLFetchUploadProgress(
     84     const net::URLFetcher* source, int64 current, int64 total) {
     85 }
     86 
     87 bool WebRtcLogUploader::ApplyForStartLogging() {
     88   DCHECK(create_thread_checker_.CalledOnValidThread());
     89   if (log_count_ < kLogCountLimit) {
     90     ++log_count_;
     91     return true;
     92   }
     93   return false;
     94 }
     95 
     96 void WebRtcLogUploader::LoggingStoppedDontUpload() {
     97   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
     98       base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
     99 }
    100 
    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());
    110 
    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);
    117 
    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;
    127   }
    128 
    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)));
    136 
    137   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
    138       base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
    139 }
    140 
    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);
    171 
    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);
    177   }
    178 
    179   AddLogData(post_data, log_buffer, log_buffer_length);
    180   net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data);
    181 }
    182 
    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");
    192 
    193   CompressLog(post_data, log_buffer, log_buffer_length);
    194 
    195   post_data->append("\r\n");
    196 }
    197 
    198 void WebRtcLogUploader::CompressLog(std::string* post_data,
    199                                     uint8* input,
    200                                     uint32 input_size) {
    201   PartialCircularBuffer read_pcb(input, input_size);
    202 
    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);
    211 
    212   uint8 intermediate_buffer[kIntermediateCompressionBufferBytes] = {0};
    213   ResizeForNextOutput(post_data, &stream);
    214   uint32 read = 0;
    215 
    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;
    224     }
    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);
    230 
    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);
    235 
    236   result = deflate(&stream, Z_FINISH);
    237   DCHECK_EQ(Z_STREAM_END, result);
    238   result = deflateEnd(&stream);
    239   DCHECK_EQ(Z_OK, result);
    240 
    241   post_data->resize(post_data->size() - stream.avail_out);
    242 }
    243 
    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;
    250 }
    251 
    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);
    260 
    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;
    267 }
    268 
    269 void WebRtcLogUploader::DecreaseLogCount() {
    270   DCHECK(create_thread_checker_.CalledOnValidThread());
    271   --log_count_;
    272 }
    273 
    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;
    279 
    280   if (base::PathExists(upload_list_path)) {
    281     bool read_ok = base::ReadFileToString(upload_list_path, &contents);
    282     DPCHECK(read_ok);
    283 
    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;
    292     }
    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);
    297     }
    298   }
    299 
    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';
    304 
    305   int written = file_util::WriteFile(upload_list_path, &contents[0],
    306                                      contents.size());
    307   DPCHECK(written == static_cast<int>(contents.size()));
    308 }
    309 
    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);
    323     }
    324     content::BrowserThread::PostTask(
    325         content::BrowserThread::UI, FROM_HERE,
    326         base::Bind(upload_done_data.callback, success, report_id,
    327                    error_message));
    328   }
    329 }
    330