Index: components/webrtc_log_uploader/webrtc_log_uploader.cc |
diff --git a/components/webrtc_log_uploader/webrtc_log_uploader.cc b/components/webrtc_log_uploader/webrtc_log_uploader.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7c2e3fde997ab5bd621c194c9d2b50cb07de1512 |
--- /dev/null |
+++ b/components/webrtc_log_uploader/webrtc_log_uploader.cc |
@@ -0,0 +1,286 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "components/webrtc_log_uploader/webrtc_log_uploader.h" |
+ |
+#include "base/file_util.h" |
+#include "base/logging.h" |
+#include "base/memory/partial_circular_buffer.h" |
+#include "base/shared_memory.h" |
+#include "base/stringprintf.h" |
+#include "content/public/browser/browser_thread.h" |
+#include "net/base/network_delegate.h" |
+#include "net/proxy/proxy_config.h" |
+#include "net/proxy/proxy_config_service.h" |
+#include "net/url_request/url_fetcher.h" |
+#include "net/url_request/url_request_context.h" |
+#include "net/url_request/url_request_context_builder.h" |
+#include "net/url_request/url_request_context_getter.h" |
+#include "third_party/zlib/zlib.h" |
+ |
+namespace components { |
+ |
+namespace { |
+ |
+const int kLogCountLimit = 5; |
+const uint32 kIntermediateCompressionBufferBytes = 256 * 1024; // 256 KB |
+ |
+const char kUploadURL[] = "https://clients2.google.com/cr/report"; |
+const char kUploadContentType[] = "multipart/form-data"; |
+const char kMultipartBoundary[] = |
+ "----**--yradnuoBgoLtrapitluMklaTelgooG--**----"; |
+ |
+// Config getter that always returns direct settings. |
+class ProxyConfigServiceDirect : public net::ProxyConfigService { |
+ public: |
+ // ProxyConfigService implementation. |
+ virtual void AddObserver(Observer* observer) OVERRIDE {} |
+ virtual void RemoveObserver(Observer* observer) OVERRIDE {} |
+ virtual ConfigAvailability GetLatestProxyConfig( |
+ net::ProxyConfig* config) OVERRIDE { |
+ *config = net::ProxyConfig::CreateDirect(); |
+ return CONFIG_VALID; |
+ } |
+}; |
+ |
+} // namespace |
+ |
+class WebRtcLogURLRequestContextGetter : public net::URLRequestContextGetter { |
+ public: |
+ WebRtcLogURLRequestContextGetter() {} |
+ |
+ // net::URLRequestContextGetter implementation. |
+ virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ |
+ if (!url_request_context_) { |
+ net::URLRequestContextBuilder builder; |
+#if defined(OS_LINUX) || defined(OS_ANDROID) |
+ builder.set_proxy_config_service(new ProxyConfigServiceDirect()); |
+#endif |
+ url_request_context_.reset(builder.Build()); |
+ CHECK(url_request_context_.get()); |
+ } |
+ |
+ return url_request_context_.get(); |
+ } |
+ |
+ virtual scoped_refptr<base::SingleThreadTaskRunner> |
+ GetNetworkTaskRunner() const OVERRIDE { |
+ return content::BrowserThread::GetMessageLoopProxyForThread( |
+ content::BrowserThread::IO); |
+ } |
+ |
+ private: |
+ virtual ~WebRtcLogURLRequestContextGetter() {} |
+ |
+ // NULL if not yet initialized. Otherwise, it is the URLRequestContext |
+ // instance that was lazily created by GetURLRequestContext(). |
+ // Access only from the IO thread. |
+ scoped_ptr<net::URLRequestContext> url_request_context_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(WebRtcLogURLRequestContextGetter); |
+}; |
+ |
+ |
+WebRtcLogUploader::WebRtcLogUploader() |
+ : log_count_(0) { |
+} |
+ |
+WebRtcLogUploader::~WebRtcLogUploader() { |
+} |
+ |
+void WebRtcLogUploader::OnURLFetchComplete( |
+ const net::URLFetcher* source) { |
+} |
+ |
+void WebRtcLogUploader::OnURLFetchUploadProgress( |
+ const net::URLFetcher* source, int64 current, int64 total) { |
+} |
+ |
+bool WebRtcLogUploader::ApplyForStartLogging() { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ if (log_count_ < kLogCountLimit) { |
+ ++log_count_; |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+void WebRtcLogUploader::UploadLog(scoped_ptr<base::SharedMemory> shared_memory, |
+ uint32 length) { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); |
+ DCHECK(shared_memory); |
+ DCHECK(shared_memory->memory()); |
+ |
+ base::FilePath upload_file_path; |
+ if (file_util::CreateTemporaryFile(&upload_file_path)) { |
+ SetupMultipartFile(reinterpret_cast<uint8*>(shared_memory->memory()), |
+ length, upload_file_path); |
+ |
+ if (!request_context_getter_) |
+ request_context_getter_ = new WebRtcLogURLRequestContextGetter(); |
+ |
+ std::string content_type = kUploadContentType; |
+ content_type.append("; boundary="); |
+ content_type.append(kMultipartBoundary); |
+ |
+ net::URLFetcher* url_fetcher = |
+ net::URLFetcher::Create(GURL(kUploadURL), net::URLFetcher::POST, this); |
+ url_fetcher->SetRequestContext(request_context_getter_); |
+ url_fetcher->SetUploadFilePath( |
+ content_type, upload_file_path, 0, kuint64max, |
+ content::BrowserThread::GetMessageLoopProxyForThread( |
+ content::BrowserThread::FILE)); |
+ url_fetcher->Start(); |
+ } else { |
+ DLOG(ERROR) << "WebRtcLogUploader could not create temporary file: " |
+ << upload_file_path.value(); |
+ } |
+ |
+ content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, |
+ base::Bind(&WebRtcLogUploader::DecreaseLogCount, base::Unretained(this))); |
+} |
+ |
+void WebRtcLogUploader::SetupMultipartFile( |
+ uint8* log_buffer, uint32 log_buffer_length, |
+ const base::FilePath& upload_file_path) { |
+ int flags = base::PLATFORM_FILE_CREATE_ALWAYS | |
+ base::PLATFORM_FILE_WRITE; |
+ bool created = false; |
+ base::PlatformFileError error = base::PLATFORM_FILE_OK; |
+ base::PlatformFile multipart_file = |
+ CreatePlatformFile(upload_file_path, flags, &created, &error); |
+ DCHECK(created); |
+ DCHECK_EQ(error, base::PLATFORM_FILE_OK); |
+ |
+ // TODO(grunell): Correct product name. |
+ AddPairString(multipart_file, "prod", "Chrome"); |
+ // TODO(grunell): Correct version. |
+ AddPairString(multipart_file, "ver", "0.0.1.1-dev-test"); |
+ AddPairString(multipart_file, "guid", "0"); |
+ AddPairString(multipart_file, "type", "log"); |
+ |
+ AddUrlChunks(); |
+ |
+ AddLogData(multipart_file, log_buffer, log_buffer_length); |
+ |
+ std::string str = "--"; |
+ str.append(kMultipartBoundary); |
+ str.append("--\r\n"); |
+ base::WritePlatformFileAtCurrentPos(multipart_file, str.c_str(), str.size()); |
+ |
+ DCHECK(base::ClosePlatformFile(multipart_file)); |
+} |
+ |
+void WebRtcLogUploader::AddPairString(base::PlatformFile multipart_file, |
+ const std::string& key, |
+ const std::string& value) { |
+ std::string str; |
+ AddMultipartValueForUpload(key, value, kMultipartBoundary, "", &str); |
+ base::WritePlatformFileAtCurrentPos(multipart_file, str.c_str(), str.size()); |
+} |
+ |
+void WebRtcLogUploader::AddUrlChunks() { |
+ // TODO(grunell): Implement. |
+} |
+ |
+void WebRtcLogUploader::AddLogData(base::PlatformFile multipart_file, |
+ uint8* log_buffer, |
+ uint32 log_buffer_length) { |
+ std::string str = "--"; |
+ str.append(kMultipartBoundary); |
+ str.append("\r\n"); |
+ str.append("Content-Disposition: form-data; name=\"log\""); |
+ str.append("; filename=\"log.gz\"\r\n"); |
+ str.append("Content-Type: application/gzip\r\n\r\n"); |
+ base::WritePlatformFileAtCurrentPos(multipart_file, str.c_str(), str.size()); |
+ |
+ CompressLog(log_buffer, log_buffer_length, multipart_file); |
+ |
+ std::string end_str = "\r\n"; |
+ base::WritePlatformFileAtCurrentPos( |
+ multipart_file, end_str.c_str(), end_str.size()); |
+} |
+ |
+// TODO(grunell): This is copied from cloud_print_helpers.cc. Break out to its |
+// own file. |
+void WebRtcLogUploader::AddMultipartValueForUpload( |
+ const std::string& value_name, const std::string& value, |
+ const std::string& mime_boundary, const std::string& content_type, |
+ std::string* post_data) { |
+ DCHECK(post_data); |
+ // First line is the boundary |
+ post_data->append("--" + mime_boundary + "\r\n"); |
+ // Next line is the Content-disposition |
+ post_data->append(base::StringPrintf("Content-Disposition: form-data; " |
+ "name=\"%s\"\r\n", value_name.c_str())); |
+ if (!content_type.empty()) { |
+ // If Content-type is specified, the next line is that |
+ post_data->append(base::StringPrintf("Content-Type: %s\r\n", |
+ content_type.c_str())); |
+ } |
+ // Leave an empty line and append the value. |
+ post_data->append(base::StringPrintf("\r\n%s\r\n", value.c_str())); |
+} |
+ |
+void WebRtcLogUploader::CompressLog(uint8* input, |
+ uint32 input_size, |
+ base::PlatformFile output_file) { |
+ base::PartialCircularBuffer read_pcb(input, input_size); |
+ |
+ z_stream stream = {0}; |
+ int result = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, |
+ // windowBits = 15 is default, 16 is added to |
+ // produce a gzip header + trailer. |
+ 15 + 16, |
+ 8, // memLevel = 8 is default. |
+ Z_DEFAULT_STRATEGY); |
+ DCHECK_EQ(Z_OK, result); |
+ |
+ scoped_ptr<uint8[]> intermediate_buffer( |
+ new uint8[kIntermediateCompressionBufferBytes]); |
+ scoped_ptr<uint8[]> compressed( |
+ new uint8[kIntermediateCompressionBufferBytes]); |
+ memset(intermediate_buffer.get(), 0, kIntermediateCompressionBufferBytes); |
+ memset(compressed.get(), 0, kIntermediateCompressionBufferBytes); |
+ |
+ char* compressed_char_ptr = reinterpret_cast<char*>(&compressed.get()[0]); |
+ uint32 read = 0; |
+ |
+ do { |
+ read = read_pcb.Read(intermediate_buffer.get(), |
+ kIntermediateCompressionBufferBytes); |
+ stream.next_in = &intermediate_buffer.get()[0]; |
+ stream.avail_in = read; |
+ stream.next_out = &compressed.get()[0]; |
+ stream.avail_out = kIntermediateCompressionBufferBytes; |
+ if (read != kIntermediateCompressionBufferBytes) |
+ break; |
+ |
+ result = deflate(&stream, Z_SYNC_FLUSH); |
+ DCHECK_EQ(Z_OK, result); |
+ DCHECK_LE(stream.avail_out, kIntermediateCompressionBufferBytes); |
+ base::WritePlatformFileAtCurrentPos( |
+ output_file, compressed_char_ptr, |
+ kIntermediateCompressionBufferBytes - stream.avail_out); |
+ } while (true); |
+ |
+ result = deflate(&stream, Z_FINISH); |
+ DCHECK(result == Z_STREAM_END); |
+ DCHECK_LE(stream.avail_out, kIntermediateCompressionBufferBytes); |
+ base::WritePlatformFileAtCurrentPos( |
+ output_file, compressed_char_ptr, |
+ kIntermediateCompressionBufferBytes - stream.avail_out); |
+ |
+ result = deflateEnd(&stream); |
+ DCHECK_EQ(Z_OK, result); |
+} |
+ |
+void WebRtcLogUploader::DecreaseLogCount() { |
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
+ --log_count_; |
+} |
+ |
+} // namespace components |