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

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: Upload again (error last upload). 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..fbba960d1b481262788636cc241578fb30e823ab
--- /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)
Jói 2013/05/14 21:30:29 nit: I think conditional includes usually come aft
Henrik Grunell 2013/05/15 20:17:53 I've replaced bzlib with zlib; the former has been
+#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
Jói 2013/05/14 21:30:29 Suggest renaming to kIntermediateCompressionBuffer
Henrik Grunell 2013/05/15 20:17:53 Done.
+
+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.
Jói 2013/05/14 21:30:29 Can you document the motivation for this? I see it
Henrik Grunell 2013/05/15 20:17:53 I'll double check with someone who knows net, back
Henrik Grunell 2013/05/24 13:01:50 I've changed it so that it uses the system context
+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) {
+ 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;
Jói 2013/05/14 21:30:29 Why are you deleting the object? I don't think ow
Henrik Grunell 2013/05/15 20:17:53 You're right, I think I got that from a unit test
+}
+
+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();
+ url_fetchers_.push_back(url_fetcher);
+ } else {
+ DLOG(ERROR) << "Could not create temporary file";
Jói 2013/05/14 21:30:29 Suggest a more informative message, i.e. that it's
Henrik Grunell 2013/05/15 20:17:53 Done.
+ }
+
+ 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(error == base::PLATFORM_FILE_OK);
Jói 2013/05/14 21:30:29 DCHECK_EQ
Henrik Grunell 2013/05/15 20:17:53 Done.
+
+ // TODO(grunell): Correct product name.
Jói 2013/05/14 21:30:29 Is this TODO (and the one 2 lines down) for the cu
Henrik Grunell 2013/05/15 20:17:53 I was thinking for the current change, but it can
Henrik Grunell 2013/05/24 13:01:50 It's done now.
+ 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.
Jói 2013/05/14 21:30:29 I'm assuming this is needed for the current change
Henrik Grunell 2013/05/15 20:17:53 Same as reply above. That's my intention but could
Henrik Grunell 2013/05/24 13:01:50 This functions has been removed; not needed.
+}
+
+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.bz2\"\r\n");
+ str.append("Content-Type: application/x-bzip\r\n\r\n");
+ base::WritePlatformFileAtCurrentPos(multipart_file, str.c_str(), str.size());
Jói 2013/05/14 21:30:29 I have to wonder, since the log_buffer is in memor
Henrik Grunell 2013/05/15 20:17:53 Very valid point. Here's why I chose a file: after
Henrik Grunell 2013/05/24 13:01:50 Changed to writing to memory instead.
+
+ 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