OLD | NEW |
(Empty) | |
| 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 "components/webrtc_log_uploader/webrtc_log_uploader.h" |
| 6 |
| 7 #include "base/file_util.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/memory/partial_circular_buffer.h" |
| 10 #include "base/shared_memory.h" |
| 11 #include "base/stringprintf.h" |
| 12 #include "content/public/browser/browser_thread.h" |
| 13 #include "net/base/network_delegate.h" |
| 14 #include "net/proxy/proxy_config.h" |
| 15 #include "net/proxy/proxy_config_service.h" |
| 16 #include "net/url_request/url_fetcher.h" |
| 17 #include "net/url_request/url_request_context.h" |
| 18 #include "net/url_request/url_request_context_builder.h" |
| 19 #include "net/url_request/url_request_context_getter.h" |
| 20 #include "third_party/zlib/zlib.h" |
| 21 |
| 22 namespace components { |
| 23 |
| 24 namespace { |
| 25 |
| 26 const int kLogCountLimit = 5; |
| 27 const uint32 kIntermediateCompressionBufferBytes = 256 * 1024; // 256 KB |
| 28 |
| 29 const char kUploadURL[] = "https://clients2.google.com/cr/report"; |
| 30 const char kUploadContentType[] = "multipart/form-data"; |
| 31 const char kMultipartBoundary[] = |
| 32 "----**--yradnuoBgoLtrapitluMklaTelgooG--**----"; |
| 33 |
| 34 // Config getter that always returns direct settings. |
| 35 class ProxyConfigServiceDirect : public net::ProxyConfigService { |
| 36 public: |
| 37 // ProxyConfigService implementation. |
| 38 virtual void AddObserver(Observer* observer) OVERRIDE {} |
| 39 virtual void RemoveObserver(Observer* observer) OVERRIDE {} |
| 40 virtual ConfigAvailability GetLatestProxyConfig( |
| 41 net::ProxyConfig* config) OVERRIDE { |
| 42 *config = net::ProxyConfig::CreateDirect(); |
| 43 return CONFIG_VALID; |
| 44 } |
| 45 }; |
| 46 |
| 47 } // namespace |
| 48 |
| 49 class WebRtcLogURLRequestContextGetter : public net::URLRequestContextGetter { |
| 50 public: |
| 51 WebRtcLogURLRequestContextGetter() {} |
| 52 |
| 53 // net::URLRequestContextGetter implementation. |
| 54 virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE { |
| 55 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
| 56 |
| 57 if (!url_request_context_) { |
| 58 net::URLRequestContextBuilder builder; |
| 59 #if defined(OS_LINUX) || defined(OS_ANDROID) |
| 60 builder.set_proxy_config_service(new ProxyConfigServiceDirect()); |
| 61 #endif |
| 62 url_request_context_.reset(builder.Build()); |
| 63 CHECK(url_request_context_.get()); |
| 64 } |
| 65 |
| 66 return url_request_context_.get(); |
| 67 } |
| 68 |
| 69 virtual scoped_refptr<base::SingleThreadTaskRunner> |
| 70 GetNetworkTaskRunner() const OVERRIDE { |
| 71 return content::BrowserThread::GetMessageLoopProxyForThread( |
| 72 content::BrowserThread::IO); |
| 73 } |
| 74 |
| 75 private: |
| 76 virtual ~WebRtcLogURLRequestContextGetter() {} |
| 77 |
| 78 // NULL if not yet initialized. Otherwise, it is the URLRequestContext |
| 79 // instance that was lazily created by GetURLRequestContext(). |
| 80 // Access only from the IO thread. |
| 81 scoped_ptr<net::URLRequestContext> url_request_context_; |
| 82 |
| 83 DISALLOW_COPY_AND_ASSIGN(WebRtcLogURLRequestContextGetter); |
| 84 }; |
| 85 |
| 86 |
| 87 WebRtcLogUploader::WebRtcLogUploader() |
| 88 : log_count_(0) { |
| 89 } |
| 90 |
| 91 WebRtcLogUploader::~WebRtcLogUploader() { |
| 92 } |
| 93 |
| 94 void WebRtcLogUploader::OnURLFetchComplete( |
| 95 const net::URLFetcher* source) { |
| 96 } |
| 97 |
| 98 void WebRtcLogUploader::OnURLFetchUploadProgress( |
| 99 const net::URLFetcher* source, int64 current, int64 total) { |
| 100 } |
| 101 |
| 102 bool WebRtcLogUploader::ApplyForStartLogging() { |
| 103 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 104 if (log_count_ < kLogCountLimit) { |
| 105 ++log_count_; |
| 106 return true; |
| 107 } |
| 108 return false; |
| 109 } |
| 110 |
| 111 void WebRtcLogUploader::UploadLog(scoped_ptr<base::SharedMemory> shared_memory, |
| 112 uint32 length) { |
| 113 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
| 114 DCHECK(shared_memory); |
| 115 DCHECK(shared_memory->memory()); |
| 116 |
| 117 base::FilePath upload_file_path; |
| 118 if (file_util::CreateTemporaryFile(&upload_file_path)) { |
| 119 SetupMultipartFile(reinterpret_cast<uint8*>(shared_memory->memory()), |
| 120 length, upload_file_path); |
| 121 |
| 122 if (!request_context_getter_) |
| 123 request_context_getter_ = new WebRtcLogURLRequestContextGetter(); |
| 124 |
| 125 std::string content_type = kUploadContentType; |
| 126 content_type.append("; boundary="); |
| 127 content_type.append(kMultipartBoundary); |
| 128 |
| 129 net::URLFetcher* url_fetcher = |
| 130 net::URLFetcher::Create(GURL(kUploadURL), net::URLFetcher::POST, this); |
| 131 url_fetcher->SetRequestContext(request_context_getter_); |
| 132 url_fetcher->SetUploadFilePath( |
| 133 content_type, upload_file_path, 0, kuint64max, |
| 134 content::BrowserThread::GetMessageLoopProxyForThread( |
| 135 content::BrowserThread::FILE)); |
| 136 url_fetcher->Start(); |
| 137 } else { |
| 138 DLOG(ERROR) << "WebRtcLogUploader could not create temporary file: " |
| 139 << upload_file_path.value(); |
| 140 } |
| 141 |
| 142 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
| 143 base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this))); |
| 144 } |
| 145 |
| 146 void WebRtcLogUploader::SetupMultipartFile( |
| 147 uint8* log_buffer, uint32 log_buffer_length, |
| 148 const base::FilePath& upload_file_path) { |
| 149 int flags = base::PLATFORM_FILE_CREATE_ALWAYS | |
| 150 base::PLATFORM_FILE_WRITE; |
| 151 bool created = false; |
| 152 base::PlatformFileError error = base::PLATFORM_FILE_OK; |
| 153 base::PlatformFile multipart_file = |
| 154 CreatePlatformFile(upload_file_path, flags, &created, &error); |
| 155 DCHECK(created); |
| 156 DCHECK_EQ(error, base::PLATFORM_FILE_OK); |
| 157 |
| 158 // TODO(grunell): Correct product name. |
| 159 AddPairString(multipart_file, "prod", "Chrome"); |
| 160 // TODO(grunell): Correct version. |
| 161 AddPairString(multipart_file, "ver", "0.0.1.1-dev-test"); |
| 162 AddPairString(multipart_file, "guid", "0"); |
| 163 AddPairString(multipart_file, "type", "log"); |
| 164 |
| 165 AddUrlChunks(); |
| 166 |
| 167 AddLogData(multipart_file, log_buffer, log_buffer_length); |
| 168 |
| 169 std::string str = "--"; |
| 170 str.append(kMultipartBoundary); |
| 171 str.append("--\r\n"); |
| 172 base::WritePlatformFileAtCurrentPos(multipart_file, str.c_str(), str.size()); |
| 173 |
| 174 DCHECK(base::ClosePlatformFile(multipart_file)); |
| 175 } |
| 176 |
| 177 void WebRtcLogUploader::AddPairString(base::PlatformFile multipart_file, |
| 178 const std::string& key, |
| 179 const std::string& value) { |
| 180 std::string str; |
| 181 AddMultipartValueForUpload(key, value, kMultipartBoundary, "", &str); |
| 182 base::WritePlatformFileAtCurrentPos(multipart_file, str.c_str(), str.size()); |
| 183 } |
| 184 |
| 185 void WebRtcLogUploader::AddUrlChunks() { |
| 186 // TODO(grunell): Implement. |
| 187 } |
| 188 |
| 189 void WebRtcLogUploader::AddLogData(base::PlatformFile multipart_file, |
| 190 uint8* log_buffer, |
| 191 uint32 log_buffer_length) { |
| 192 std::string str = "--"; |
| 193 str.append(kMultipartBoundary); |
| 194 str.append("\r\n"); |
| 195 str.append("Content-Disposition: form-data; name=\"log\""); |
| 196 str.append("; filename=\"log.gz\"\r\n"); |
| 197 str.append("Content-Type: application/gzip\r\n\r\n"); |
| 198 base::WritePlatformFileAtCurrentPos(multipart_file, str.c_str(), str.size()); |
| 199 |
| 200 CompressLog(log_buffer, log_buffer_length, multipart_file); |
| 201 |
| 202 std::string end_str = "\r\n"; |
| 203 base::WritePlatformFileAtCurrentPos( |
| 204 multipart_file, end_str.c_str(), end_str.size()); |
| 205 } |
| 206 |
| 207 // TODO(grunell): This is copied from cloud_print_helpers.cc. Break out to its |
| 208 // own file. |
| 209 void WebRtcLogUploader::AddMultipartValueForUpload( |
| 210 const std::string& value_name, const std::string& value, |
| 211 const std::string& mime_boundary, const std::string& content_type, |
| 212 std::string* post_data) { |
| 213 DCHECK(post_data); |
| 214 // First line is the boundary |
| 215 post_data->append("--" + mime_boundary + "\r\n"); |
| 216 // Next line is the Content-disposition |
| 217 post_data->append(base::StringPrintf("Content-Disposition: form-data; " |
| 218 "name=\"%s\"\r\n", value_name.c_str())); |
| 219 if (!content_type.empty()) { |
| 220 // If Content-type is specified, the next line is that |
| 221 post_data->append(base::StringPrintf("Content-Type: %s\r\n", |
| 222 content_type.c_str())); |
| 223 } |
| 224 // Leave an empty line and append the value. |
| 225 post_data->append(base::StringPrintf("\r\n%s\r\n", value.c_str())); |
| 226 } |
| 227 |
| 228 void WebRtcLogUploader::CompressLog(uint8* input, |
| 229 uint32 input_size, |
| 230 base::PlatformFile output_file) { |
| 231 base::PartialCircularBuffer read_pcb(input, input_size); |
| 232 |
| 233 z_stream stream = {0}; |
| 234 int result = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, |
| 235 // windowBits = 15 is default, 16 is added to |
| 236 // produce a gzip header + trailer. |
| 237 15 + 16, |
| 238 8, // memLevel = 8 is default. |
| 239 Z_DEFAULT_STRATEGY); |
| 240 DCHECK_EQ(Z_OK, result); |
| 241 |
| 242 scoped_ptr<uint8[]> intermediate_buffer( |
| 243 new uint8[kIntermediateCompressionBufferBytes]); |
| 244 scoped_ptr<uint8[]> compressed( |
| 245 new uint8[kIntermediateCompressionBufferBytes]); |
| 246 memset(intermediate_buffer.get(), 0, kIntermediateCompressionBufferBytes); |
| 247 memset(compressed.get(), 0, kIntermediateCompressionBufferBytes); |
| 248 |
| 249 char* compressed_char_ptr = reinterpret_cast<char*>(&compressed.get()[0]); |
| 250 uint32 read = 0; |
| 251 |
| 252 do { |
| 253 read = read_pcb.Read(intermediate_buffer.get(), |
| 254 kIntermediateCompressionBufferBytes); |
| 255 stream.next_in = &intermediate_buffer.get()[0]; |
| 256 stream.avail_in = read; |
| 257 stream.next_out = &compressed.get()[0]; |
| 258 stream.avail_out = kIntermediateCompressionBufferBytes; |
| 259 if (read != kIntermediateCompressionBufferBytes) |
| 260 break; |
| 261 |
| 262 result = deflate(&stream, Z_SYNC_FLUSH); |
| 263 DCHECK_EQ(Z_OK, result); |
| 264 DCHECK_LE(stream.avail_out, kIntermediateCompressionBufferBytes); |
| 265 base::WritePlatformFileAtCurrentPos( |
| 266 output_file, compressed_char_ptr, |
| 267 kIntermediateCompressionBufferBytes - stream.avail_out); |
| 268 } while (true); |
| 269 |
| 270 result = deflate(&stream, Z_FINISH); |
| 271 DCHECK(result == Z_STREAM_END); |
| 272 DCHECK_LE(stream.avail_out, kIntermediateCompressionBufferBytes); |
| 273 base::WritePlatformFileAtCurrentPos( |
| 274 output_file, compressed_char_ptr, |
| 275 kIntermediateCompressionBufferBytes - stream.avail_out); |
| 276 |
| 277 result = deflateEnd(&stream); |
| 278 DCHECK_EQ(Z_OK, result); |
| 279 } |
| 280 |
| 281 void WebRtcLogUploader::DecreaseLogCount() { |
| 282 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
| 283 --log_count_; |
| 284 } |
| 285 |
| 286 } // namespace components |
OLD | NEW |