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 HpackEncoder
* encoder
= ObtainEncoder(SpdySessionKey(
53 HostPortPair::FromURL(request
.url
), proxy
, request
.privacy_mode
));
55 // Convert and encode the request and response header sets.
57 SpdyHeaderBlock headers
;
58 CreateSpdyHeadersFromHttpRequest(
59 request
, request_headers
, SPDY4
, false, &headers
);
62 encoder
->EncodeHeaderSet(headers
, &tmp_out
);
65 SpdyHeaderBlock headers
;
66 CreateSpdyHeadersFromHttpResponse(response_headers
, &headers
);
69 encoder
->EncodeHeaderSet(headers
, &tmp_out
);
71 if (total_counts_
>= kTotalCountsPublishThreshold
) {
77 bool HpackHuffmanAggregator::UseAggregator() {
78 const std::string group_name
=
79 base::FieldTrialList::FindFullName("HpackHuffmanAggregator");
80 if (group_name
== "Enabled") {
87 void HpackHuffmanAggregator::CreateSpdyHeadersFromHttpResponse(
88 const HttpResponseHeaders
& headers
,
89 SpdyHeaderBlock
* headers_out
) {
90 // Lower-case header names, and coalesce multiple values delimited by \0.
91 // Also add the fixed status header.
92 std::string name
, value
;
94 while (headers
.EnumerateHeaderLines(&it
, &name
, &value
)) {
95 base::StringToLowerASCII(&name
);
96 if (headers_out
->find(name
) == headers_out
->end()) {
97 (*headers_out
)[name
] = value
;
99 (*headers_out
)[name
] += std::string(1, '\0') + value
;
102 (*headers_out
)[":status"] = base::IntToString(headers
.response_code());
106 bool HpackHuffmanAggregator::IsCrossOrigin(const HttpRequestInfo
& request
) {
107 // Require that the request is top-level, or that it shares
108 // an origin with its referer.
109 if ((request
.load_flags
& LOAD_MAIN_FRAME
) == 0) {
110 std::string referer_str
;
111 if (!request
.extra_headers
.GetHeader(HttpRequestHeaders::kReferer
,
113 // Require a referer.
116 GURL
referer(referer_str
);
117 if (!HostPortPair::FromURL(request
.url
).Equals(
118 HostPortPair::FromURL(referer
))) {
119 // Cross-origin request.
126 HpackEncoder
* HpackHuffmanAggregator::ObtainEncoder(const SpdySessionKey
& key
) {
127 for (OriginEncoders::iterator it
= encoders_
.begin();
128 it
!= encoders_
.end(); ++it
) {
129 if (key
.Equals(it
->first
)) {
130 // Move to head of list and return.
131 OriginEncoder origin_encoder
= *it
;
133 encoders_
.push_front(origin_encoder
);
134 return origin_encoder
.second
;
137 // Not found. Create a new encoder, evicting one if needed.
138 encoders_
.push_front(std::make_pair(
139 key
, new HpackEncoder(ObtainHpackHuffmanTable())));
140 if (encoders_
.size() > max_encoders_
) {
141 delete encoders_
.back().second
;
142 encoders_
.pop_back();
144 encoders_
.front().second
->SetCharCountsStorage(&counts_
, &total_counts_
);
145 return encoders_
.front().second
;
148 void HpackHuffmanAggregator::PublishCounts() {
149 // base::Histogram requires that values be 1-indexed.
150 const size_t kRangeMin
= 1;
151 const size_t kRangeMax
= counts_
.size() + 1;
152 const size_t kBucketCount
= kRangeMax
+ 1;
154 base::BucketRanges
ranges(kBucketCount
+ 1);
155 for (size_t i
= 0; i
!= ranges
.size(); ++i
) {
156 ranges
.set_range(i
, i
);
158 ranges
.ResetChecksum();
160 // Copy |counts_| into a SampleVector.
161 base::SampleVector
samples(&ranges
);
162 for (size_t i
= 0; i
!= counts_
.size(); ++i
) {
163 samples
.Accumulate(i
+ 1, counts_
[i
]);
166 STATIC_HISTOGRAM_POINTER_BLOCK(
169 base::LinearHistogram::FactoryGet(
170 kHistogramName
, kRangeMin
, kRangeMax
, kBucketCount
,
171 base::HistogramBase::kUmaTargetedHistogramFlag
));
174 counts_
.assign(counts_
.size(), 0);