Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(429)

Unified Diff: components/webrtc_log_uploader/webrtc_log_uploader.cc

Issue 14329020: Implementing uploading of a WebRTC diagnostic log. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Code review. Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..2983f39f956f1dc99183d1bb79198828b85a63b9
--- /dev/null
+++ b/components/webrtc_log_uploader/webrtc_log_uploader.cc
@@ -0,0 +1,299 @@
+// 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"
+
+#if defined(USE_SYSTEM_LIBBZ2)
+#include <bzlib.h>
+#else
+#include "third_party/bzip2/bzlib.h"
+#endif
+
+#include "base/file_util.h"
+#include "base/logging.h"
+#include "base/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"
+
+namespace components {
+
+namespace {
+
+const int kLogCountLimit = 5;
+const uint32 kIntermediateCompressionBufferSize = 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());
vabr (Chromium) 2013/05/13 08:55:50 nit: Put this CHECK into the if branch above?
Henrik Grunell 2013/05/14 13:48:50 Done.
+
+ 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),
+ multipart_file_(base::kInvalidPlatformFileValue) {
+}
+
+WebRtcLogUploader::~WebRtcLogUploader() {
+}
+
+void WebRtcLogUploader::OnURLFetchComplete(
+ const net::URLFetcher* source) {
+ std::vector<net::URLFetcher*>::iterator it;
+ for (it = url_fetchers_.begin(); it != url_fetchers_.end(); ++it) {
+ if (*it == source) {
+ url_fetchers_.erase(it);
+ break;
+ }
+ }
+ delete 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);
vabr (Chromium) 2013/05/13 08:55:50 |content_type| looks like a composition of compile
Henrik Grunell 2013/05/14 13:48:50 Yes it could (and I agree it's nicer), but the bou
vabr (Chromium) 2013/05/14 15:24:41 I see three possibilities: 1. #define kMultipartB
vabr (Chromium) 2013/05/14 15:24:41 On 2013/05/14 13:48:50, Henrik Grunell wrote: > On
Henrik Grunell 2013/05/15 20:17:53 Yes I did think of these options, I prefer 3 (simp
+
+ 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();
+ url_fetchers_.push_back(url_fetcher);
+ } else {
+ DLOG(ERROR) << "Could not create temporary file";
+ }
+
+ 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;
+ multipart_file_ =
+ CreatePlatformFile(upload_file_path, flags, &created, &error);
+ DCHECK(created);
+ DCHECK(error == base::PLATFORM_FILE_OK);
+
+ // TODO(grunell): Correct product name.
+ AddPairString("prod", "Chrome");
+ // TODO(grunell): Correct version.
+ AddPairString("ver", "0.0.1.1-dev-test");
+ AddPairString("guid", "0");
+ AddPairString("type", "log");
+
+ AddUrlChunks();
+
+ AddLogData(log_buffer, log_buffer_length);
+
+ std::stringstream ss;
+ ss << "--" << kMultipartBoundary << "--" << "\r\n";
+ base::WritePlatformFileAtCurrentPos(
+ multipart_file_, ss.str().data(), ss.str().size());
+
+ DCHECK(base::ClosePlatformFile(multipart_file_));
+}
+
+void WebRtcLogUploader::AddPairString(const std::string& key,
+ const std::string& value) {
vabr (Chromium) 2013/05/13 08:55:50 nit: correct indenting
Henrik Grunell 2013/05/14 13:48:50 Done.
+ 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(uint8* log_buffer,
+ uint32 log_buffer_length) {
+ std::stringstream ss;
vabr (Chromium) 2013/05/13 08:55:50 I don't think the Style Guide allows streams http:
Henrik Grunell 2013/05/14 13:48:50 OK, sure. Done.
+ ss << "--" << kMultipartBoundary << "\r\n";
+ ss << "Content-Disposition: form-data; name=\"log\"";
+ ss << "; filename=\"log.bz2\"" << "\r\n";
+ ss << "Content-Type: application/x-bzip" << "\r\n";
+ ss << "\r\n";
+ base::WritePlatformFileAtCurrentPos(
+ multipart_file_, ss.str().data(), ss.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);
+
+ bz_stream stream = {0};
+ int result = BZ2_bzCompressInit(&stream, 3, 0, 0);
+ DCHECK(result == BZ_OK);
+
+ scoped_ptr<uint8[]> intermediate_buffer(
+ new uint8[kIntermediateCompressionBufferSize]);
+ scoped_ptr<uint8[]> compressed(
+ new uint8[kIntermediateCompressionBufferSize]);
+ memset(intermediate_buffer.get(), 0, kIntermediateCompressionBufferSize);
+ memset(compressed.get(), 0, kIntermediateCompressionBufferSize);
+ uint32 read = read_pcb.Read(intermediate_buffer.get(),
+ kIntermediateCompressionBufferSize);
+
+ stream.next_in = reinterpret_cast<char*>(&intermediate_buffer.get()[0]);
+ stream.avail_in = read;
+ char* compressed_char_ptr = reinterpret_cast<char*>(&compressed.get()[0]);
+
+ // Keeps track of written output.
+ unsigned int last_total_out = stream.total_out_lo32;
+
+ while (read == kIntermediateCompressionBufferSize) {
+ stream.next_out = compressed_char_ptr;
+ stream.avail_out = kIntermediateCompressionBufferSize;
+ result = BZ2_bzCompress(&stream, BZ_RUN);
+ DCHECK(result == BZ_RUN_OK);
+ base::WritePlatformFileAtCurrentPos(output_file, compressed_char_ptr,
+ stream.total_out_lo32 - last_total_out);
+ last_total_out = stream.total_out_lo32;
+ read = read_pcb.Read(intermediate_buffer.get(),
+ kIntermediateCompressionBufferSize);
+ stream.next_in = reinterpret_cast<char*>(&intermediate_buffer.get()[0]);
+ stream.avail_in = read;
+ }
+
+ do {
+ stream.next_out = compressed_char_ptr;
+ stream.avail_out = kIntermediateCompressionBufferSize;
+ result = BZ2_bzCompress(&stream, BZ_FINISH);
+ base::WritePlatformFileAtCurrentPos(output_file, compressed_char_ptr,
+ stream.total_out_lo32 - last_total_out);
+ last_total_out = stream.total_out_lo32;
+ } while (result == BZ_FINISH_OK);
+ DCHECK(result == BZ_STREAM_END);
+
+ result = BZ2_bzCompressEnd(&stream);
+ DCHECK(result == BZ_OK);
+}
+
+void WebRtcLogUploader::DecreaseLogCount() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ --log_count_;
+}
+
+} // namespace components

Powered by Google App Engine
This is Rietveld 408576698