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

Unified Diff: sync/internal_api/http_bridge.cc

Issue 1246523003: [Sync] Finch Experiment : Enable compression between sync client and server (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix compile warning Created 5 years, 5 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
« no previous file with comments | « no previous file | sync/internal_api/http_bridge_unittest.cc » ('j') | sync/internal_api/http_bridge_unittest.cc » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sync/internal_api/http_bridge.cc
diff --git a/sync/internal_api/http_bridge.cc b/sync/internal_api/http_bridge.cc
index c64822d5836d2e729d6f7bca00069e5eebbd7c80..b10121f124694bd8b4e66a1fc77372dede30e8fd 100644
--- a/sync/internal_api/http_bridge.cc
+++ b/sync/internal_api/http_bridge.cc
@@ -5,6 +5,7 @@
#include "sync/internal_api/public/http_bridge.h"
#include "base/message_loop/message_loop.h"
+#include "base/metrics/field_trial.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
@@ -35,8 +36,167 @@ void LogTimeout(bool timed_out) {
UMA_HISTOGRAM_BOOLEAN("Sync.URLFetchTimedOut", timed_out);
}
+bool IsSyncHttpContentCompressionEnabled() {
+ const std::string group_name =
+ base::FieldTrialList::FindFullName("SyncHttpContentCompression");
+ return group_name == "Enabled";
+}
+
+// The difference in bytes between a zlib header and a gzip header.
Nicolas Zea 2015/07/30 19:59:44 Add a comment that explains that makes it clear wh
Gang Wu 2015/07/31 00:39:45 Done.
+const size_t kGzipZlibHeaderDifferenceBytes = 16;
+
+// Pass an integer greater than the following get a gzip header instead of a
+// zlib header when calling deflateInit2() and inflateInit2().
+const int kWindowBitsToGetGzipHeader = 16;
+
+// This describes the amount of memory zlib uses to compress data. It can go
+// from 1 to 9, with 8 being the default. For details, see:
+// http://www.zlib.net/manual.html (search for memLevel).
+const int kZlibMemoryLevel = 8;
+
+// This code is taken almost verbatim from third_party/zlib/compress.c. The only
+// difference is deflateInit2() is called which sets the window bits to be > 16.
+// That causes a gzip header to be emitted rather than a zlib header.
+int GzipCompressHelper(Bytef* dest,
+ uLongf* dest_length,
+ const Bytef* source,
+ uLong source_length) {
+ z_stream stream;
+
+ stream.next_in = bit_cast<Bytef*>(source);
+ stream.avail_in = static_cast<uInt>(source_length);
+ stream.next_out = dest;
+ stream.avail_out = static_cast<uInt>(*dest_length);
+ if (static_cast<uLong>(stream.avail_out) != *dest_length)
+ return Z_BUF_ERROR;
+
+ stream.zalloc = static_cast<alloc_func>(0);
+ stream.zfree = static_cast<free_func>(0);
+ stream.opaque = static_cast<voidpf>(0);
+
+ gz_header gzip_header;
+ memset(&gzip_header, 0, sizeof(gzip_header));
+ int err = deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
+ MAX_WBITS + kWindowBitsToGetGzipHeader,
+ kZlibMemoryLevel, Z_DEFAULT_STRATEGY);
+ if (err != Z_OK)
+ return err;
+
+ err = deflateSetHeader(&stream, &gzip_header);
+ if (err != Z_OK)
+ return err;
+
+ err = deflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ deflateEnd(&stream);
+ return err == Z_OK ? Z_BUF_ERROR : err;
+ }
+ *dest_length = stream.total_out;
+
+ err = deflateEnd(&stream);
+ return err;
+}
+
+// This code is taken almost verbatim from third_party/zlib/uncompr.c. The only
+// difference is inflateInit2() is called which sets the window bits to be > 16.
+// That causes a gzip header to be parsed rather than a zlib header.
+int GzipUncompressHelper(Bytef* dest,
+ uLongf* dest_length,
+ const Bytef* source,
+ uLong source_length) {
+ z_stream stream;
+
+ stream.next_in = bit_cast<Bytef*>(source);
+ stream.avail_in = static_cast<uInt>(source_length);
+ if (static_cast<uLong>(stream.avail_in) != source_length)
+ return Z_BUF_ERROR;
+
+ stream.next_out = dest;
+ stream.avail_out = static_cast<uInt>(*dest_length);
+ if (static_cast<uLong>(stream.avail_out) != *dest_length)
+ return Z_BUF_ERROR;
+
+ stream.zalloc = static_cast<alloc_func>(0);
+ stream.zfree = static_cast<free_func>(0);
+
+ int err = inflateInit2(&stream, MAX_WBITS + kWindowBitsToGetGzipHeader);
+ if (err != Z_OK)
+ return err;
+
+ err = inflate(&stream, Z_FINISH);
+ if (err != Z_STREAM_END) {
+ inflateEnd(&stream);
+ if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0))
+ return Z_DATA_ERROR;
+ return err;
+ }
+ *dest_length = stream.total_out;
+
+ err = inflateEnd(&stream);
+ return err;
+}
+
+// Returns the uncompressed size from GZIP-compressed |compressed_data|.
+uint32 GetUncompressedSize(const std::string& compressed_data) {
+ // The uncompressed size is stored in the last 4 bytes of |input| in LE.
+ uint32 size;
+ if (compressed_data.length() < sizeof(size))
+ return 0;
+ memcpy(&size, &compressed_data[compressed_data.length() - sizeof(size)],
+ sizeof(size));
+ return base::ByteSwapToLE32(size);
+}
+
+void RecordSyncRequestContentLengthHistograms(int64 compressed_content_length,
+ int64 original_content_length) {
+ UMA_HISTOGRAM_COUNTS("Sync.RequestContentLengthCompressed",
+ compressed_content_length);
+ UMA_HISTOGRAM_COUNTS("Sync.RequestContentLengthOriginal",
+ original_content_length);
+}
+
+void RecordSyncResponseContentLengthHistograms(int64 compressed_content_length,
+ int64 original_content_length) {
+ UMA_HISTOGRAM_COUNTS("Sync.ResponseContentLengthCompressed",
+ compressed_content_length);
+ UMA_HISTOGRAM_COUNTS("Sync.ResponseContentLengthOriginal",
+ original_content_length);
+}
+
} // namespace
+bool GzipCompress(const std::string& input, std::string* output) {
+ const uLongf input_size = static_cast<uLongf>(input.size());
+ std::vector<Bytef> compressed_data(kGzipZlibHeaderDifferenceBytes +
+ compressBound(input_size));
+
+ uLongf compressed_size = static_cast<uLongf>(compressed_data.size());
+ if (GzipCompressHelper(&compressed_data.front(), &compressed_size,
+ bit_cast<const Bytef*>(input.data()),
+ input_size) != Z_OK) {
+ return false;
+ }
+
+ compressed_data.resize(compressed_size);
+ output->assign(compressed_data.begin(), compressed_data.end());
+ DCHECK_EQ(input_size, GetUncompressedSize(*output));
+ return true;
+}
+
+bool GzipUncompress(const std::string& input, std::string* output) {
+ std::string uncompressed_output;
+ uLongf uncompressed_size = static_cast<uLongf>(GetUncompressedSize(input));
+ uncompressed_output.resize(uncompressed_size);
+ if (GzipUncompressHelper(bit_cast<Bytef*>(uncompressed_output.data()),
+ &uncompressed_size,
+ bit_cast<const Bytef*>(input.data()),
+ static_cast<uLongf>(input.length())) == Z_OK) {
+ output->swap(uncompressed_output);
+ return true;
+ }
+ return false;
+}
+
HttpBridgeFactory::HttpBridgeFactory(
const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
const NetworkTimeUpdateCallback& network_time_update_callback,
@@ -203,19 +363,35 @@ void HttpBridge::MakeAsynchronousPost() {
base::Bind(&HttpBridge::OnURLFetchTimedOut, this));
DCHECK(request_context_getter_.get());
+ fetch_state_.start_time = base::Time::Now();
fetch_state_.url_poster =
net::URLFetcher::Create(url_for_request_, net::URLFetcher::POST, this)
.release();
fetch_state_.url_poster->SetRequestContext(request_context_getter_.get());
- fetch_state_.url_poster->SetUploadData(content_type_, request_content_);
fetch_state_.url_poster->SetExtraRequestHeaders(extra_headers_);
+
+ int compressed_content_size = 0;
Nicolas Zea 2015/07/30 19:59:44 You're using an int here, but the RequestXXXMethod
Gang Wu 2015/07/31 00:39:45 int64, since GetContentLength() return int64 it mi
+ if (IsSyncHttpContentCompressionEnabled()) {
+ std::string compressed_request_content;
+ GzipCompress(request_content_, &compressed_request_content);
+ compressed_content_size = compressed_request_content.size();
+ fetch_state_.url_poster->SetUploadData(content_type_,
+ compressed_request_content);
+ fetch_state_.url_poster->AddExtraRequestHeader("Content-Encoding: gzip");
+ } else {
+ fetch_state_.url_poster->SetUploadData(content_type_, request_content_);
+ fetch_state_.url_poster->AddExtraRequestHeader("Accept-Encoding: deflate");
+ }
+
+ RecordSyncRequestContentLengthHistograms(compressed_content_size,
+ request_content_.size());
+
fetch_state_.url_poster->AddExtraRequestHeader(base::StringPrintf(
"%s: %s", net::HttpRequestHeaders::kUserAgent, user_agent_.c_str()));
fetch_state_.url_poster->SetLoadFlags(net::LOAD_BYPASS_CACHE |
net::LOAD_DISABLE_CACHE |
net::LOAD_DO_NOT_SAVE_COOKIES |
net::LOAD_DO_NOT_SEND_COOKIES);
- fetch_state_.start_time = base::Time::Now();
fetch_state_.url_poster->Start();
}
@@ -315,6 +491,17 @@ void HttpBridge::OnURLFetchComplete(const net::URLFetcher* source) {
fetch_state_.response_headers = source->GetResponseHeaders();
UpdateNetworkTime();
+ int64 compressed_content_length = fetch_state_.response_content.size();
Nicolas Zea 2015/07/30 19:59:44 Same comment above about type consistency/correctn
Gang Wu 2015/07/31 00:39:45 Done.
+ int64 original_content_length = compressed_content_length;
+ if (fetch_state_.response_headers &&
+ fetch_state_.response_headers->HasHeaderValue("content-encoding",
+ "gzip")) {
+ compressed_content_length =
+ fetch_state_.response_headers->GetContentLength();
+ }
+ RecordSyncResponseContentLengthHistograms(compressed_content_length,
+ original_content_length);
+
// End of the line for url_poster_. It lives only on the IO loop.
// We defer deletion because we're inside a callback from a component of the
// URLFetcher, so it seems most natural / "polite" to let the stack unwind.
« no previous file with comments | « no previous file | sync/internal_api/http_bridge_unittest.cc » ('j') | sync/internal_api/http_bridge_unittest.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698