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