• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 #include "net/spdy/hpack_huffman_aggregator.h"
5 
6 #include "base/metrics/bucket_ranges.h"
7 #include "base/metrics/field_trial.h"
8 #include "base/metrics/histogram.h"
9 #include "base/metrics/sample_vector.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "net/base/load_flags.h"
14 #include "net/http/http_request_headers.h"
15 #include "net/http/http_request_info.h"
16 #include "net/http/http_response_headers.h"
17 #include "net/spdy/hpack_encoder.h"
18 #include "net/spdy/spdy_http_utils.h"
19 
20 namespace net {
21 
22 namespace {
23 
24 const char kHistogramName[] = "Net.SpdyHpackEncodedCharacterFrequency";
25 
26 const size_t kTotalCountsPublishThreshold = 50000;
27 
28 // Each encoder uses the default dynamic table size of 4096 total bytes.
29 const size_t kMaxEncoders = 20;
30 
31 }  // namespace
32 
HpackHuffmanAggregator()33 HpackHuffmanAggregator::HpackHuffmanAggregator()
34   : counts_(256, 0),
35     total_counts_(0),
36     max_encoders_(kMaxEncoders) {
37 }
38 
~HpackHuffmanAggregator()39 HpackHuffmanAggregator::~HpackHuffmanAggregator() {
40   STLDeleteContainerPairSecondPointers(encoders_.begin(), encoders_.end());
41   encoders_.clear();
42 }
43 
AggregateTransactionCharacterCounts(const HttpRequestInfo & request,const HttpRequestHeaders & request_headers,const ProxyServer & proxy,const HttpResponseHeaders & response_headers)44 void HpackHuffmanAggregator::AggregateTransactionCharacterCounts(
45     const HttpRequestInfo& request,
46     const HttpRequestHeaders& request_headers,
47     const ProxyServer& proxy,
48     const HttpResponseHeaders& response_headers) {
49   if (IsCrossOrigin(request)) {
50     return;
51   }
52   HostPortPair endpoint = HostPortPair(request.url.HostNoBrackets(),
53                                        request.url.EffectiveIntPort());
54   HpackEncoder* encoder = ObtainEncoder(
55       SpdySessionKey(endpoint, proxy, request.privacy_mode));
56 
57   // Convert and encode the request and response header sets.
58   {
59     SpdyHeaderBlock headers;
60     CreateSpdyHeadersFromHttpRequest(
61         request, request_headers, SPDY4, false, &headers);
62 
63     std::string tmp_out;
64     encoder->EncodeHeaderSet(headers, &tmp_out);
65   }
66   {
67     SpdyHeaderBlock headers;
68     CreateSpdyHeadersFromHttpResponse(response_headers, &headers);
69 
70     std::string tmp_out;
71     encoder->EncodeHeaderSet(headers, &tmp_out);
72   }
73   if (total_counts_ >= kTotalCountsPublishThreshold) {
74     PublishCounts();
75   }
76 }
77 
78 // static
UseAggregator()79 bool HpackHuffmanAggregator::UseAggregator() {
80   const std::string group_name =
81       base::FieldTrialList::FindFullName("HpackHuffmanAggregator");
82   if (group_name == "Enabled") {
83     return true;
84   }
85   return false;
86 }
87 
88 // static
CreateSpdyHeadersFromHttpResponse(const HttpResponseHeaders & headers,SpdyHeaderBlock * headers_out)89 void HpackHuffmanAggregator::CreateSpdyHeadersFromHttpResponse(
90     const HttpResponseHeaders& headers,
91     SpdyHeaderBlock* headers_out) {
92   // Lower-case header names, and coalesce multiple values delimited by \0.
93   // Also add the fixed status header.
94   std::string name, value;
95   void* it = NULL;
96   while (headers.EnumerateHeaderLines(&it, &name, &value)) {
97     base::StringToLowerASCII(&name);
98     if (headers_out->find(name) == headers_out->end()) {
99       (*headers_out)[name] = value;
100     } else {
101       (*headers_out)[name] += std::string(1, '\0') + value;
102     }
103   }
104   (*headers_out)[":status"] = base::IntToString(headers.response_code());
105 }
106 
107 // static
IsCrossOrigin(const HttpRequestInfo & request)108 bool HpackHuffmanAggregator::IsCrossOrigin(const HttpRequestInfo& request) {
109   // Require that the request is top-level, or that it shares
110   // an origin with its referer.
111   HostPortPair endpoint = HostPortPair(request.url.HostNoBrackets(),
112                                        request.url.EffectiveIntPort());
113   if ((request.load_flags & LOAD_MAIN_FRAME) == 0) {
114     std::string referer_str;
115     if (!request.extra_headers.GetHeader(HttpRequestHeaders::kReferer,
116                                          &referer_str)) {
117       // Require a referer.
118       return true;
119     }
120     GURL referer(referer_str);
121     HostPortPair referer_endpoint = HostPortPair(referer.HostNoBrackets(),
122                                                  referer.EffectiveIntPort());
123     if (!endpoint.Equals(referer_endpoint)) {
124       // Cross-origin request.
125       return true;
126     }
127   }
128   return false;
129 }
130 
ObtainEncoder(const SpdySessionKey & key)131 HpackEncoder* HpackHuffmanAggregator::ObtainEncoder(const SpdySessionKey& key) {
132   for (OriginEncoders::iterator it = encoders_.begin();
133        it != encoders_.end(); ++it) {
134     if (key.Equals(it->first)) {
135       // Move to head of list and return.
136       OriginEncoder origin_encoder = *it;
137       encoders_.erase(it);
138       encoders_.push_front(origin_encoder);
139       return origin_encoder.second;
140     }
141   }
142   // Not found. Create a new encoder, evicting one if needed.
143   encoders_.push_front(std::make_pair(
144       key, new HpackEncoder(ObtainHpackHuffmanTable())));
145   if (encoders_.size() > max_encoders_) {
146     delete encoders_.back().second;
147     encoders_.pop_back();
148   }
149   encoders_.front().second->SetCharCountsStorage(&counts_, &total_counts_);
150   return encoders_.front().second;
151 }
152 
PublishCounts()153 void HpackHuffmanAggregator::PublishCounts() {
154   // base::Histogram requires that values be 1-indexed.
155   const size_t kRangeMin = 1;
156   const size_t kRangeMax = counts_.size() + 1;
157   const size_t kBucketCount = kRangeMax + 1;
158 
159   base::BucketRanges ranges(kBucketCount + 1);
160   for (size_t i = 0; i != ranges.size(); ++i) {
161     ranges.set_range(i, i);
162   }
163   ranges.ResetChecksum();
164 
165   // Copy |counts_| into a SampleVector.
166   base::SampleVector samples(&ranges);
167   for (size_t i = 0; i != counts_.size(); ++i) {
168     samples.Accumulate(i + 1, counts_[i]);
169   }
170 
171   STATIC_HISTOGRAM_POINTER_BLOCK(
172       kHistogramName,
173       AddSamples(samples),
174       base::LinearHistogram::FactoryGet(
175           kHistogramName, kRangeMin, kRangeMax, kBucketCount,
176           base::HistogramBase::kUmaTargetedHistogramFlag));
177 
178   // Clear counts.
179   counts_.assign(counts_.size(), 0);
180   total_counts_ = 0;
181 }
182 
183 }  // namespace net
184