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/histogram.h"
7 #include "base/metrics/statistics_recorder.h"
8 #include "net/base/load_flags.h"
9 #include "net/http/http_request_headers.h"
10 #include "net/http/http_request_info.h"
11 #include "net/http/http_response_headers.h"
12 #include "testing/gmock/include/gmock/gmock.h"
13 #include "testing/gtest/include/gtest/gtest.h"
17 using ::testing::Each
;
18 using ::testing::ElementsAre
;
20 using ::testing::Pair
;
23 const char kHistogramName
[] = "Net.SpdyHpackEncodedCharacterFrequency";
28 class HpackHuffmanAggregatorPeer
{
30 explicit HpackHuffmanAggregatorPeer(HpackHuffmanAggregator
* agg
)
33 std::vector
<size_t>* counts() {
34 return &agg_
->counts_
;
36 HpackHuffmanAggregator::OriginEncoders
* encoders() {
37 return &agg_
->encoders_
;
39 size_t total_counts() {
40 return agg_
->total_counts_
;
42 void set_total_counts(size_t total_counts
) {
43 agg_
->total_counts_
= total_counts
;
45 void set_max_encoders(size_t max_encoders
) {
46 agg_
->max_encoders_
= max_encoders
;
48 static bool IsCrossOrigin(const HttpRequestInfo
& request
) {
49 return HpackHuffmanAggregator::IsCrossOrigin(request
);
51 static void CreateSpdyHeadersFromHttpResponse(
52 const HttpResponseHeaders
& headers
,
53 SpdyHeaderBlock
* headers_out
) {
54 HpackHuffmanAggregator::CreateSpdyHeadersFromHttpResponse(
55 headers
, headers_out
);
57 HpackEncoder
* ObtainEncoder(const SpdySessionKey
& key
) {
58 return agg_
->ObtainEncoder(key
);
60 void PublishCounts() {
61 agg_
->PublishCounts();
65 HpackHuffmanAggregator
* agg_
;
70 class HpackHuffmanAggregatorTest
: public ::testing::Test
{
72 HpackHuffmanAggregatorTest()
75 HpackHuffmanAggregator agg_
;
76 test::HpackHuffmanAggregatorPeer peer_
;
79 TEST_F(HpackHuffmanAggregatorTest
, CrossOriginDetermination
) {
80 HttpRequestInfo request
;
81 request
.url
= GURL("https://www.foo.com/a/page");
83 // Main load without referer.
84 request
.load_flags
= LOAD_MAIN_FRAME
;
85 EXPECT_FALSE(peer_
.IsCrossOrigin(request
));
87 // Non-main load without referer. Treated as cross-origin.
88 request
.load_flags
= 0;
89 EXPECT_TRUE(peer_
.IsCrossOrigin(request
));
91 // Main load with different referer origin.
92 request
.load_flags
= LOAD_MAIN_FRAME
;
93 request
.extra_headers
.SetHeader(HttpRequestHeaders::kReferer
,
94 "https://www.bar.com/other/page");
95 EXPECT_FALSE(peer_
.IsCrossOrigin(request
));
97 // Non-main load with different referer orign.
98 request
.load_flags
= 0;
99 EXPECT_TRUE(peer_
.IsCrossOrigin(request
));
101 // Non-main load with same referer orign.
102 request
.extra_headers
.SetHeader(HttpRequestHeaders::kReferer
,
103 "https://www.foo.com/other/page");
104 EXPECT_FALSE(peer_
.IsCrossOrigin(request
));
106 // Non-main load with same referer host but different schemes.
107 request
.extra_headers
.SetHeader(HttpRequestHeaders::kReferer
,
108 "http://www.foo.com/other/page");
109 EXPECT_TRUE(peer_
.IsCrossOrigin(request
));
112 TEST_F(HpackHuffmanAggregatorTest
, EncoderLRUQueue
) {
113 peer_
.set_max_encoders(2);
115 SpdySessionKey
key1(HostPortPair("one.com", 443), ProxyServer::Direct(),
116 PRIVACY_MODE_ENABLED
);
117 SpdySessionKey
key2(HostPortPair("two.com", 443), ProxyServer::Direct(),
118 PRIVACY_MODE_ENABLED
);
119 SpdySessionKey
key3(HostPortPair("three.com", 443), ProxyServer::Direct(),
120 PRIVACY_MODE_ENABLED
);
123 HpackEncoder
* one
= peer_
.ObtainEncoder(key1
);
124 EXPECT_EQ(1u, peer_
.encoders()->size());
126 // Creates two.com. No evictions.
127 HpackEncoder
* two
= peer_
.ObtainEncoder(key2
);
128 EXPECT_EQ(2u, peer_
.encoders()->size());
132 EXPECT_EQ(one
, peer_
.ObtainEncoder(key1
));
134 // Creates three.com. Evicts two.com, as it's least-recently used.
135 HpackEncoder
* three
= peer_
.ObtainEncoder(key3
);
136 EXPECT_EQ(one
, peer_
.ObtainEncoder(key1
));
137 EXPECT_NE(one
, three
);
138 EXPECT_EQ(2u, peer_
.encoders()->size());
141 TEST_F(HpackHuffmanAggregatorTest
, PublishCounts
) {
142 (*peer_
.counts())[0] = 1;
143 (*peer_
.counts())[255] = 10;
144 (*peer_
.counts())[128] = 101;
145 peer_
.set_total_counts(112);
147 peer_
.PublishCounts();
149 // Internal counts were reset after being published.
150 EXPECT_THAT(*peer_
.counts(), Each(Eq(0u)));
151 EXPECT_EQ(0u, peer_
.total_counts());
153 // Verify histogram counts match the expectation.
154 scoped_ptr
<base::HistogramSamples
> samples
=
155 base::StatisticsRecorder::FindHistogram(kHistogramName
)
158 EXPECT_EQ(0, samples
->GetCount(0));
159 EXPECT_EQ(1, samples
->GetCount(1));
160 EXPECT_EQ(101, samples
->GetCount(129));
161 EXPECT_EQ(10, samples
->GetCount(256));
162 EXPECT_EQ(112, samples
->TotalCount());
164 // Publish a second round of counts;
165 (*peer_
.counts())[1] = 32;
166 (*peer_
.counts())[128] = 5;
167 peer_
.set_total_counts(37);
169 peer_
.PublishCounts();
171 // Verify they've been aggregated into the previous counts.
172 samples
= base::StatisticsRecorder::FindHistogram(kHistogramName
)
175 EXPECT_EQ(0, samples
->GetCount(0));
176 EXPECT_EQ(1, samples
->GetCount(1));
177 EXPECT_EQ(32, samples
->GetCount(2));
178 EXPECT_EQ(106, samples
->GetCount(129));
179 EXPECT_EQ(10, samples
->GetCount(256));
180 EXPECT_EQ(149, samples
->TotalCount());
183 TEST_F(HpackHuffmanAggregatorTest
, CreateSpdyResponseHeaders
) {
185 "HTTP/1.1 202 Accepted \0"
186 "Content-TYPE : text/html; charset=utf-8 \0"
187 "Set-Cookie: foo=bar \0"
188 "Set-Cookie: baz=bing \0"
189 "Cache-Control: pragma=no-cache \0"
190 "Cache-CONTROL: expires=12345 \0\0";
192 scoped_refptr
<HttpResponseHeaders
> parsed_headers(new HttpResponseHeaders(
193 std::string(kRawHeaders
, arraysize(kRawHeaders
) - 1)));
195 SpdyHeaderBlock headers
;
196 peer_
.CreateSpdyHeadersFromHttpResponse(*parsed_headers
, &headers
);
197 EXPECT_THAT(headers
, ElementsAre(
198 Pair(":status", "202"),
199 Pair("cache-control", std::string("pragma=no-cache\0expires=12345", 29)),
200 Pair("content-type", "text/html; charset=utf-8"),
201 Pair("set-cookie", std::string("foo=bar\0baz=bing", 16))));