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"
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"
24 const char kHistogramName
[] = "Net.SpdyHpackEncodedCharacterFrequency";
26 const size_t kTotalCountsPublishThreshold
= 50000;
28 // Each encoder uses the default dynamic table size of 4096 total bytes.
29 const size_t kMaxEncoders
= 20;
33 HpackHuffmanAggregator::HpackHuffmanAggregator()
36 max_encoders_(kMaxEncoders
) {
39 HpackHuffmanAggregator::~HpackHuffmanAggregator() {
40 STLDeleteContainerPairSecondPointers(encoders_
.begin(), encoders_
.end());
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
)) {
52 HostPortPair endpoint
= HostPortPair(request
.url
.HostNoBrackets(),
53 request
.url
.EffectiveIntPort());
54 HpackEncoder
* encoder
= ObtainEncoder(
55 SpdySessionKey(endpoint
, proxy
, request
.privacy_mode
));
57 // Convert and encode the request and response header sets.
59 SpdyHeaderBlock headers
;
60 CreateSpdyHeadersFromHttpRequest(
61 request
, request_headers
, SPDY4
, false, &headers
);
64 encoder
->EncodeHeaderSet(headers
, &tmp_out
);
67 SpdyHeaderBlock headers
;
68 CreateSpdyHeadersFromHttpResponse(response_headers
, &headers
);
71 encoder
->EncodeHeaderSet(headers
, &tmp_out
);
73 if (total_counts_
>= kTotalCountsPublishThreshold
) {
79 bool HpackHuffmanAggregator::UseAggregator() {
80 const std::string group_name
=
81 base::FieldTrialList::FindFullName("HpackHuffmanAggregator");
82 if (group_name
== "Enabled") {
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
;
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
;
101 (*headers_out
)[name
] += std::string(1, '\0') + value
;
104 (*headers_out
)[":status"] = base::IntToString(headers
.response_code());
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
,
117 // Require a referer.
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.
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
;
138 encoders_
.push_front(origin_encoder
);
139 return origin_encoder
.second
;
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();
149 encoders_
.front().second
->SetCharCountsStorage(&counts_
, &total_counts_
);
150 return encoders_
.front().second
;
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;
159 base::BucketRanges
ranges(kBucketCount
+ 1);
160 for (size_t i
= 0; i
!= ranges
.size(); ++i
) {
161 ranges
.set_range(i
, i
);
163 ranges
.ResetChecksum();
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
]);
171 STATIC_HISTOGRAM_POINTER_BLOCK(
174 base::LinearHistogram::FactoryGet(
175 kHistogramName
, kRangeMin
, kRangeMax
, kBucketCount
,
176 base::HistogramBase::kUmaTargetedHistogramFlag
));
179 counts_
.assign(counts_
.size(), 0);