Index: net/spdy/hpack_huffman_aggregator.cc |
diff --git a/net/spdy/hpack_huffman_aggregator.cc b/net/spdy/hpack_huffman_aggregator.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9923fa148a63d14c3a5179c22e6d70edb833ccbf |
--- /dev/null |
+++ b/net/spdy/hpack_huffman_aggregator.cc |
@@ -0,0 +1,184 @@ |
+// Copyright 2014 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 "net/spdy/hpack_huffman_aggregator.h" |
+ |
+#include "base/metrics/bucket_ranges.h" |
+#include "base/metrics/field_trial.h" |
+#include "base/metrics/histogram.h" |
+#include "base/metrics/sample_vector.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/strings/string_util.h" |
+#include "net/base/load_flags.h" |
+#include "net/http/http_request_headers.h" |
+#include "net/http/http_request_info.h" |
+#include "net/http/http_response_headers.h" |
+#include "net/spdy/hpack_encoder.h" |
+#include "net/spdy/spdy_http_utils.h" |
+ |
+namespace net { |
+ |
+namespace { |
+ |
+const char kHistogramName[] = "Net.SpdyHpackEncodedCharacterFrequency"; |
+ |
+const size_t kTotalCountsPublishThreshold = 10000; |
+ |
+// Each encoder uses the default dynamic table size of 4096 total bytes. |
+const size_t kMaxEncoders = 20; |
+ |
+} // namespace |
+ |
+HpackHuffmanAggregator::HpackHuffmanAggregator() |
+ : counts_(256, 0), |
+ total_counts_(0), |
+ max_encoders_(kMaxEncoders) { |
+} |
+ |
+HpackHuffmanAggregator::~HpackHuffmanAggregator() { |
+} |
+ |
+void HpackHuffmanAggregator::AggregateTransactionCharacterCounts( |
+ const HttpRequestInfo& request, |
+ const HttpRequestHeaders& request_headers, |
+ const ProxyServer& proxy, |
+ const HttpResponseHeaders& response_headers) { |
+ if (IsCrossOrigin(request)) { |
+ return; |
+ } |
+ HostPortPair endpoint = HostPortPair(request.url.HostNoBrackets(), |
+ request.url.EffectiveIntPort()); |
+ HpackEncoder* encoder = ObtainEncoder( |
+ SpdySessionKey(endpoint, proxy, request.privacy_mode)); |
+ |
+ // Convert and encode the request and response header sets. |
+ { |
+ SpdyHeaderBlock headers; |
+ CreateSpdyHeadersFromHttpRequest( |
+ request, request_headers, &headers, SPDY4, false); |
+ |
+ std::string tmp_out; |
+ encoder->EncodeHeaderSet(headers, &tmp_out); |
+ } |
+ { |
+ SpdyHeaderBlock headers; |
+ CreateSpdyHeadersFromHttpResponse(response_headers, &headers); |
+ |
+ std::string tmp_out; |
+ encoder->EncodeHeaderSet(headers, &tmp_out); |
+ } |
+ if (total_counts_ >= kTotalCountsPublishThreshold) { |
+ PublishCounts(); |
+ } |
+} |
+ |
+// static |
+bool HpackHuffmanAggregator::UseAggregator() { |
+ const std::string group_name = |
+ base::FieldTrialList::FindFullName("HpackHuffmanAggregator"); |
+ if (group_name == "Enabled") { |
+ return true; |
+ } |
+ // HACK(jgraettinger): To be removed before submission. |
jar (doing other things)
2014/04/18 21:14:53
hmmmm.... this seems like a bad plan.... and subje
|
+ // This is here to enable by-default on try-bots. |
+ if (group_name.empty()) { |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+// static |
+void HpackHuffmanAggregator::CreateSpdyHeadersFromHttpResponse( |
+ const HttpResponseHeaders& headers, |
+ SpdyHeaderBlock* headers_out) { |
+ // Lower-case header names, and coalesce multiple values delimited by \0. |
+ // Also add the fixed status header. |
+ std::string name, value; |
+ void* it = NULL; |
+ while (headers.EnumerateHeaderLines(&it, &name, &value)) { |
+ StringToLowerASCII(&name); |
+ if (headers_out->find(name) == headers_out->end()) { |
+ (*headers_out)[name] = value; |
+ } else { |
+ (*headers_out)[name] += std::string(1, '\0') + value; |
+ } |
+ } |
+ (*headers_out)[":status"] = base::IntToString(headers.response_code()); |
+} |
+ |
+// static |
+bool HpackHuffmanAggregator::IsCrossOrigin(const HttpRequestInfo& request) { |
+ // Require that the request is top-level, or that it shares |
+ // an origin with its referer. |
+ HostPortPair endpoint = HostPortPair(request.url.HostNoBrackets(), |
+ request.url.EffectiveIntPort()); |
+ if ((request.load_flags & LOAD_MAIN_FRAME) == 0) { |
+ std::string referer_str; |
+ if (!request.extra_headers.GetHeader(HttpRequestHeaders::kReferer, |
+ &referer_str)) { |
+ // Require a referer. |
+ return true; |
+ } |
+ GURL referer(referer_str); |
+ HostPortPair referer_endpoint = HostPortPair(referer.HostNoBrackets(), |
+ referer.EffectiveIntPort()); |
+ if (!endpoint.Equals(referer_endpoint)) { |
+ // Cross-origin request. |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+HpackEncoder* HpackHuffmanAggregator::ObtainEncoder(const SpdySessionKey& key) { |
+ for (OriginEncoders::iterator it = encoders_.begin(); |
+ it != encoders_.end(); ++it) { |
+ if (key.Equals(it->first)) { |
+ // Move to head of list and return. |
+ OriginEncoder origin_encoder = *it; |
+ encoders_.erase(it); |
+ encoders_.push_front(origin_encoder); |
+ return origin_encoder.second; |
+ } |
+ } |
+ // Not found. Create a new encoder, evicting one if needed. |
+ encoders_.push_front(std::make_pair(key, new HpackEncoder())); |
+ if (encoders_.size() > max_encoders_) { |
+ delete encoders_.back().second; |
+ encoders_.pop_back(); |
+ } |
+ encoders_.front().second->SetCharCountsStorage(&counts_, &total_counts_); |
+ return encoders_.front().second; |
+} |
+ |
+void HpackHuffmanAggregator::PublishCounts() { |
+ // base::Histogram requires that values be 1-indexed. |
+ const size_t kRangeMin = 1; |
+ const size_t kRangeMax = counts_.size() + 1; |
+ const size_t kBucketCount = kRangeMax + 1; |
+ |
+ base::BucketRanges ranges(kBucketCount + 1); |
+ for (size_t i = 0; i != ranges.size(); ++i) { |
+ ranges.set_range(i, i); |
+ } |
+ ranges.ResetChecksum(); |
+ |
+ // Copy |counts_| into a SampleVector. |
+ base::SampleVector samples(&ranges); |
+ for (size_t i = 0; i != counts_.size(); ++i) { |
+ samples.Accumulate(i + 1, counts_[i]); |
jar (doing other things)
2014/04/18 21:14:53
Why are you putting samples into your personal his
|
+ } |
+ |
+ STATIC_HISTOGRAM_POINTER_BLOCK( |
+ kHistogramName, |
+ AddSamples(samples), |
+ base::LinearHistogram::FactoryGet( |
+ kHistogramName, kRangeMin, kRangeMax, kBucketCount, |
+ base::HistogramBase::kUmaTargetedHistogramFlag)); |
+ |
+ // Clear counts. |
+ counts_.assign(counts_.size(), 0); |
+ total_counts_ = 0; |
+} |
+ |
+} // namespace net |