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 }  // namespace
     43 
     44 WebRtcLogUploader::WebRtcLogUploader()
     45     : log_count_(0),
     46       post_data_(NULL) {
     47   base::FilePath log_dir_path;
     48   PathService::Get(chrome::DIR_USER_DATA, &log_dir_path);
     49   upload_list_path_ =
     50       log_dir_path.AppendASCII(WebRtcLogUploadList::kWebRtcLogListFilename);
     51 }
     52 
     53 WebRtcLogUploader::~WebRtcLogUploader() {}
     54 
     55 void WebRtcLogUploader::OnURLFetchComplete(
     56     const net::URLFetcher* source) {
     57   int response_code = source->GetResponseCode();
     58   std::string report_id;
     59   if (response_code == 200 && source->GetResponseAsString(&report_id))
     60     AddUploadedLogInfoToUploadListFile(report_id);
     61 }
     62 
     63 void WebRtcLogUploader::OnURLFetchUploadProgress(
     64     const net::URLFetcher* source, int64 current, int64 total) {
     65 }
     66 
     67 bool WebRtcLogUploader::ApplyForStartLogging() {
     68   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     69   if (log_count_ < kLogCountLimit) {
     70     ++log_count_;
     71     return true;
     72   }
     73   return false;
     74 }
     75 
     76 void WebRtcLogUploader::UploadLog(net::URLRequestContextGetter* request_context,
     77                                   scoped_ptr<base::SharedMemory> shared_memory,
     78                                   uint32 length,
     79                                   const std::string& app_session_id,
     80                                   const std::string& app_url) {
     81   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
     82   DCHECK(shared_memory);
     83   DCHECK(shared_memory->memory());
     84 
     85   std::string post_data;
     86   SetupMultipart(&post_data, reinterpret_cast<uint8*>(shared_memory->memory()),
     87                  length, app_session_id, app_url);
     88 
     89   // If a test has set the test string pointer, write to it and skip uploading.
     90   // This will be removed when the browser test for this feature is fully done
     91   // according to the test plan. See http://crbug.com/257329.
     92   if (post_data_) {
     93     *post_data_ = post_data;
     94     return;
     95   }
     96 
     97   std::string content_type = kUploadContentType;
     98   content_type.append("; boundary=");
     99   content_type.append(kMultipartBoundary);
    100 
    101   net::URLFetcher* url_fetcher =
    102       net::URLFetcher::Create(GURL(kUploadURL), net::URLFetcher::POST, this);
    103   url_fetcher->SetRequestContext(request_context);
    104   url_fetcher->SetUploadData(content_type, post_data);
    105   url_fetcher->Start();
    106 
    107   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
    108       base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this)));
    109 }
    110 
    111 void WebRtcLogUploader::SetupMultipart(std::string* post_data,
    112                                        uint8* log_buffer,
    113                                        uint32 log_buffer_length,
    114                                        const std::string& app_session_id,
    115                                        const std::string& app_url) {
    116 #if defined(OS_WIN)
    117   const char product[] = "Chrome";
    118 #elif defined(OS_MACOSX)
    119   const char product[] = "Chrome_Mac";
    120 #elif defined(OS_LINUX)
    121 #if !defined(ADDRESS_SANITIZER)
    122   const char product[] = "Chrome_Linux";
    123 #else
    124   const char product[] = "Chrome_Linux_ASan";
    125 #endif
    126 #elif defined(OS_ANDROID)
    127   const char product[] = "Chrome_Android";
    128 #elif defined(OS_CHROMEOS)
    129   const char product[] = "Chrome_ChromeOS";
    130 #else
    131   // This file should not be compiled for other platforms.
    132   COMPILE_ASSERT(false);
    133 #endif
    134   net::AddMultipartValueForUpload("prod", product, kMultipartBoundary,
    135                                   "", post_data);
    136   chrome::VersionInfo version_info;
    137   net::AddMultipartValueForUpload("ver", version_info.Version(),
    138                                   kMultipartBoundary, "", post_data);
    139   net::AddMultipartValueForUpload("guid", "0", kMultipartBoundary,
    140                                   "", post_data);
    141   net::AddMultipartValueForUpload("type", "webrtc_log", kMultipartBoundary,
    142                                   "", post_data);
    143   net::AddMultipartValueForUpload("app_session_id", app_session_id,
    144                                   kMultipartBoundary, "", post_data);
    145   net::AddMultipartValueForUpload("url", app_url, kMultipartBoundary,
    146                                   "", post_data);
    147   AddLogData(post_data, log_buffer, log_buffer_length);
    148   net::AddMultipartFinalDelimiterForUpload(kMultipartBoundary, post_data);
    149 }
    150 
    151 void WebRtcLogUploader::AddLogData(std::string* post_data,
    152                                    uint8* log_buffer,
    153                                    uint32 log_buffer_length) {
    154   post_data->append("--");
    155   post_data->append(kMultipartBoundary);
    156   post_data->append("\r\n");
    157   post_data->append("Content-Disposition: form-data; name=\"webrtc_log\"");
    158   post_data->append("; filename=\"webrtc_log.gz\"\r\n");
    159   post_data->append("Content-Type: application/gzip\r\n\r\n");
    160 
    161   CompressLog(post_data, log_buffer, log_buffer_length);
    162 
    163   post_data->append("\r\n");
    164 }
    165 
    166 void WebRtcLogUploader::CompressLog(std::string* post_data,
    167                                     uint8* input,
    168                                     uint32 input_size) {
    169   PartialCircularBuffer read_pcb(input, input_size);
    170 
    171   z_stream stream = {0};
    172   int result = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
    173                             // windowBits = 15 is default, 16 is added to
    174                             // produce a gzip header + trailer.
    175                             15 + 16,
    176                             8,  // memLevel = 8 is default.
    177                             Z_DEFAULT_STRATEGY);
    178   DCHECK_EQ(Z_OK, result);
    179 
    180   uint8 intermediate_buffer[kIntermediateCompressionBufferBytes] = {0};
    181   ResizeForNextOutput(post_data, &stream);
    182   uint32 read = 0;
    183 
    184   do {
    185     if (stream.avail_in == 0) {
    186       read = read_pcb.Read(&intermediate_buffer[0],
    187                            kIntermediateCompressionBufferBytes);
    188       stream.next_in = &intermediate_buffer[0];
    189       stream.avail_in = read;
    190       if (read != kIntermediateCompressionBufferBytes)
    191         break;
    192     }
    193     result = deflate(&stream, Z_SYNC_FLUSH);
    194     DCHECK_EQ(Z_OK, result);
    195     if (stream.avail_out == 0)
    196       ResizeForNextOutput(post_data, &stream);
    197   } while (true);
    198 
    199   // Ensure we have enough room in the output buffer. Easier to always just do a
    200   // resize than looping around and resize if needed.
    201   if (stream.avail_out < kIntermediateCompressionBufferBytes)
    202     ResizeForNextOutput(post_data, &stream);
    203 
    204   result = deflate(&stream, Z_FINISH);
    205   DCHECK_EQ(Z_STREAM_END, result);
    206   result = deflateEnd(&stream);
    207   DCHECK_EQ(Z_OK, result);
    208 
    209   post_data->resize(post_data->size() - stream.avail_out);
    210 }
    211 
    212 void WebRtcLogUploader::ResizeForNextOutput(std::string* post_data,
    213                                             z_stream* stream) {
    214   size_t old_size = post_data->size() - stream->avail_out;
    215   post_data->resize(old_size + kIntermediateCompressionBufferBytes);
    216   stream->next_out = reinterpret_cast<uint8*>(&(*post_data)[old_size]);
    217   stream->avail_out = kIntermediateCompressionBufferBytes;
    218 }
    219 
    220 void WebRtcLogUploader::DecreaseLogCount() {
    221   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    222   --log_count_;
    223 }
    224 
    225 void WebRtcLogUploader::AddUploadedLogInfoToUploadListFile(
    226     const std::string& report_id) {
    227   std::string contents;
    228 
    229   if (base::PathExists(upload_list_path_)) {
    230     bool read_ok = file_util::ReadFileToString(upload_list_path_, &contents);
    231     DPCHECK(read_ok);
    232 
    233     // Limit the number of log entries to |kLogListLimitLines| - 1, to make room
    234     // for the new entry. Each line including the last ends with a '\n', so hit
    235     // n will be before line n-1 (from the back).
    236     int lf_count = 0;
    237     int i = contents.size() - 1;
    238     for (; i >= 0 && lf_count < kLogListLimitLines; --i) {
    239       if (contents[i] == '\n')
    240         ++lf_count;
    241     }
    242     if (lf_count >= kLogListLimitLines) {
    243       // + 1 to compensate for the for loop decrease before the conditional
    244       // check and + 1 to get the length.
    245       contents.erase(0, i + 2);
    246     }
    247   }
    248 
    249   // Write the Unix time and report ID to the log list file.
    250   base::Time time_now = base::Time::Now();
    251   contents += base::DoubleToString(time_now.ToDoubleT()) +
    252               "," + report_id + '\n';
    253 
    254   int written = file_util::WriteFile(upload_list_path_, &contents[0],
    255                                      contents.size());
    256   DPCHECK(written == static_cast<int>(contents.size()));
    257 }
    258