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