• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/spdy/http2_push_promise_index.h"
6 
7 #include "net/base/host_port_pair.h"
8 #include "net/base/privacy_mode.h"
9 #include "net/dns/public/secure_dns_policy.h"
10 #include "net/socket/socket_tag.h"
11 #include "net/test/gtest_util.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13 #include "url/gurl.h"
14 
15 // For simplicity, these tests do not create SpdySession instances
16 // (necessary for a non-null WeakPtr<SpdySession>), instead they use nullptr.
17 // Streams are identified by spdy::SpdyStreamId only.
18 
19 using ::testing::Return;
20 using ::testing::_;
21 
22 namespace net::test {
23 namespace {
24 
25 // Delegate implementation for tests that requires exact match of SpdySessionKey
26 // in ValidatePushedStream().  Note that SpdySession, unlike TestDelegate,
27 // allows cross-origin pooling.
28 class TestDelegate : public Http2PushPromiseIndex::Delegate {
29  public:
30   TestDelegate() = delete;
TestDelegate(const SpdySessionKey & key)31   explicit TestDelegate(const SpdySessionKey& key) : key_(key) {}
32   ~TestDelegate() override = default;
33 
ValidatePushedStream(spdy::SpdyStreamId stream_id,const GURL & url,const HttpRequestInfo & request_info,const SpdySessionKey & key) const34   bool ValidatePushedStream(spdy::SpdyStreamId stream_id,
35                             const GURL& url,
36                             const HttpRequestInfo& request_info,
37                             const SpdySessionKey& key) const override {
38     return key == key_;
39   }
40 
GetWeakPtrToSession()41   base::WeakPtr<SpdySession> GetWeakPtrToSession() override { return nullptr; }
42 
43  private:
44   SpdySessionKey key_;
45 };
46 
47 }  // namespace
48 
49 class Http2PushPromiseIndexPeer {
50  public:
51   using UnclaimedPushedStream = Http2PushPromiseIndex::UnclaimedPushedStream;
52   using CompareByUrl = Http2PushPromiseIndex::CompareByUrl;
53 };
54 
55 class Http2PushPromiseIndexTest : public testing::Test {
56  protected:
Http2PushPromiseIndexTest()57   Http2PushPromiseIndexTest()
58       : url1_("https://www.example.org"),
59         url2_("https://mail.example.com"),
60         key1_(HostPortPair::FromURL(url1_),
61               ProxyServer::Direct(),
62               PRIVACY_MODE_ENABLED,
63               SpdySessionKey::IsProxySession::kFalse,
64               SocketTag(),
65               NetworkAnonymizationKey(),
66               SecureDnsPolicy::kAllow),
67         key2_(HostPortPair::FromURL(url2_),
68               ProxyServer::Direct(),
69               PRIVACY_MODE_ENABLED,
70               SpdySessionKey::IsProxySession::kFalse,
71               SocketTag(),
72               NetworkAnonymizationKey(),
73               SecureDnsPolicy::kAllow) {}
74 
75   const GURL url1_;
76   const GURL url2_;
77   const SpdySessionKey key1_;
78   const SpdySessionKey key2_;
79   Http2PushPromiseIndex index_;
80 };
81 
82 // RegisterUnclaimedPushedStream() returns false
83 // if there is already a registered entry with same delegate and URL.
TEST_F(Http2PushPromiseIndexTest,CannotRegisterSameEntryTwice)84 TEST_F(Http2PushPromiseIndexTest, CannotRegisterSameEntryTwice) {
85   TestDelegate delegate(key1_);
86   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 2, &delegate));
87   EXPECT_FALSE(index_.RegisterUnclaimedPushedStream(url1_, 4, &delegate));
88   // Unregister first entry so that DCHECK() does not fail in destructor.
89   EXPECT_TRUE(index_.UnregisterUnclaimedPushedStream(url1_, 2, &delegate));
90 }
91 
92 // UnregisterUnclaimedPushedStream() returns false
93 // if there is no identical entry registered.
94 // Case 1: no streams for the given URL.
TEST_F(Http2PushPromiseIndexTest,CannotUnregisterNonexistingEntry)95 TEST_F(Http2PushPromiseIndexTest, CannotUnregisterNonexistingEntry) {
96   TestDelegate delegate(key1_);
97   EXPECT_FALSE(index_.UnregisterUnclaimedPushedStream(url1_, 2, &delegate));
98 }
99 
100 // UnregisterUnclaimedPushedStream() returns false
101 // if there is no identical entry registered.
102 // Case 2: there is a stream for the given URL with the same Delegate,
103 // but the stream ID does not match.
TEST_F(Http2PushPromiseIndexTest,CannotUnregisterEntryIfStreamIdDoesNotMatch)104 TEST_F(Http2PushPromiseIndexTest, CannotUnregisterEntryIfStreamIdDoesNotMatch) {
105   TestDelegate delegate(key1_);
106   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 2, &delegate));
107   EXPECT_FALSE(index_.UnregisterUnclaimedPushedStream(url1_, 4, &delegate));
108   // Unregister first entry so that DCHECK() does not fail in destructor.
109   EXPECT_TRUE(index_.UnregisterUnclaimedPushedStream(url1_, 2, &delegate));
110 }
111 
112 // UnregisterUnclaimedPushedStream() returns false
113 // if there is no identical entry registered.
114 // Case 3: there is a stream for the given URL with the same stream ID,
115 // but the delegate does not match.
TEST_F(Http2PushPromiseIndexTest,CannotUnregisterEntryIfDelegateDoesNotMatch)116 TEST_F(Http2PushPromiseIndexTest, CannotUnregisterEntryIfDelegateDoesNotMatch) {
117   TestDelegate delegate1(key1_);
118   TestDelegate delegate2(key2_);
119   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 2, &delegate1));
120   EXPECT_FALSE(index_.UnregisterUnclaimedPushedStream(url1_, 2, &delegate2));
121   // Unregister first entry so that DCHECK() does not fail in destructor.
122   EXPECT_TRUE(index_.UnregisterUnclaimedPushedStream(url1_, 2, &delegate1));
123 }
124 
TEST_F(Http2PushPromiseIndexTest,CountStreamsForSession)125 TEST_F(Http2PushPromiseIndexTest, CountStreamsForSession) {
126   TestDelegate delegate1(key1_);
127   TestDelegate delegate2(key2_);
128 
129   EXPECT_EQ(0u, index_.CountStreamsForSession(&delegate1));
130   EXPECT_EQ(0u, index_.CountStreamsForSession(&delegate2));
131 
132   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 2, &delegate1));
133 
134   EXPECT_EQ(1u, index_.CountStreamsForSession(&delegate1));
135   EXPECT_EQ(0u, index_.CountStreamsForSession(&delegate2));
136 
137   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url2_, 4, &delegate1));
138 
139   EXPECT_EQ(2u, index_.CountStreamsForSession(&delegate1));
140   EXPECT_EQ(0u, index_.CountStreamsForSession(&delegate2));
141 
142   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 6, &delegate2));
143 
144   EXPECT_EQ(2u, index_.CountStreamsForSession(&delegate1));
145   EXPECT_EQ(1u, index_.CountStreamsForSession(&delegate2));
146 
147   EXPECT_TRUE(index_.UnregisterUnclaimedPushedStream(url1_, 2, &delegate1));
148 
149   EXPECT_EQ(1u, index_.CountStreamsForSession(&delegate1));
150   EXPECT_EQ(1u, index_.CountStreamsForSession(&delegate2));
151 
152   EXPECT_TRUE(index_.UnregisterUnclaimedPushedStream(url2_, 4, &delegate1));
153 
154   EXPECT_EQ(0u, index_.CountStreamsForSession(&delegate1));
155   EXPECT_EQ(1u, index_.CountStreamsForSession(&delegate2));
156 
157   EXPECT_TRUE(index_.UnregisterUnclaimedPushedStream(url1_, 6, &delegate2));
158 
159   EXPECT_EQ(0u, index_.CountStreamsForSession(&delegate1));
160   EXPECT_EQ(0u, index_.CountStreamsForSession(&delegate2));
161 }
162 
TEST_F(Http2PushPromiseIndexTest,FindStream)163 TEST_F(Http2PushPromiseIndexTest, FindStream) {
164   TestDelegate delegate1(key1_);
165   TestDelegate delegate2(key2_);
166 
167   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url1_, &delegate1));
168   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url2_, &delegate1));
169   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url1_, &delegate2));
170   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url2_, &delegate2));
171 
172   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 2, &delegate1));
173 
174   EXPECT_EQ(2u, index_.FindStream(url1_, &delegate1));
175   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url2_, &delegate1));
176   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url1_, &delegate2));
177   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url2_, &delegate2));
178 
179   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url2_, 4, &delegate1));
180 
181   EXPECT_EQ(2u, index_.FindStream(url1_, &delegate1));
182   EXPECT_EQ(4u, index_.FindStream(url2_, &delegate1));
183   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url1_, &delegate2));
184   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url2_, &delegate2));
185 
186   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 6, &delegate2));
187 
188   EXPECT_EQ(2u, index_.FindStream(url1_, &delegate1));
189   EXPECT_EQ(4u, index_.FindStream(url2_, &delegate1));
190   EXPECT_EQ(6u, index_.FindStream(url1_, &delegate2));
191   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url2_, &delegate2));
192 
193   EXPECT_TRUE(index_.UnregisterUnclaimedPushedStream(url1_, 2, &delegate1));
194 
195   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url1_, &delegate1));
196   EXPECT_EQ(4u, index_.FindStream(url2_, &delegate1));
197   EXPECT_EQ(6u, index_.FindStream(url1_, &delegate2));
198   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url2_, &delegate2));
199 
200   EXPECT_TRUE(index_.UnregisterUnclaimedPushedStream(url2_, 4, &delegate1));
201 
202   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url1_, &delegate1));
203   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url2_, &delegate1));
204   EXPECT_EQ(6u, index_.FindStream(url1_, &delegate2));
205   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url2_, &delegate2));
206 
207   EXPECT_TRUE(index_.UnregisterUnclaimedPushedStream(url1_, 6, &delegate2));
208 
209   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url1_, &delegate1));
210   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url2_, &delegate1));
211   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url1_, &delegate2));
212   EXPECT_EQ(kNoPushedStreamFound, index_.FindStream(url2_, &delegate2));
213 }
214 
215 // If |index_| is empty, then ClaimPushedStream() should set its |stream_id|
216 // outparam to kNoPushedStreamFound for any values of inparams.
TEST_F(Http2PushPromiseIndexTest,Empty)217 TEST_F(Http2PushPromiseIndexTest, Empty) {
218   base::WeakPtr<SpdySession> session;
219   spdy::SpdyStreamId stream_id = 2;
220   index_.ClaimPushedStream(key1_, url1_, HttpRequestInfo(), &session,
221                            &stream_id);
222   EXPECT_EQ(kNoPushedStreamFound, stream_id);
223 
224   stream_id = 2;
225   index_.ClaimPushedStream(key1_, url2_, HttpRequestInfo(), &session,
226                            &stream_id);
227   EXPECT_EQ(kNoPushedStreamFound, stream_id);
228 
229   stream_id = 2;
230   index_.ClaimPushedStream(key1_, url2_, HttpRequestInfo(), &session,
231                            &stream_id);
232   EXPECT_EQ(kNoPushedStreamFound, stream_id);
233 
234   stream_id = 2;
235   index_.ClaimPushedStream(key2_, url2_, HttpRequestInfo(), &session,
236                            &stream_id);
237   EXPECT_EQ(kNoPushedStreamFound, stream_id);
238 }
239 
240 // Create two entries, both with a delegate that requires |key| to be equal to
241 // |key1_|.  Register the two entries with different URLs.  Check that they can
242 // be found by their respective URLs.
TEST_F(Http2PushPromiseIndexTest,FindMultipleStreamsWithDifferentUrl)243 TEST_F(Http2PushPromiseIndexTest, FindMultipleStreamsWithDifferentUrl) {
244   // Register first entry.
245   TestDelegate delegate1(key1_);
246   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 2, &delegate1));
247 
248   // No entry found for |url2_|.
249   base::WeakPtr<SpdySession> session;
250   spdy::SpdyStreamId stream_id = 2;
251   index_.ClaimPushedStream(key1_, url2_, HttpRequestInfo(), &session,
252                            &stream_id);
253   EXPECT_EQ(kNoPushedStreamFound, stream_id);
254 
255   // Claim first entry.
256   stream_id = kNoPushedStreamFound;
257   index_.ClaimPushedStream(key1_, url1_, HttpRequestInfo(), &session,
258                            &stream_id);
259   EXPECT_EQ(2u, stream_id);
260 
261   // ClaimPushedStream() unregistered first entry, cannot claim it again.
262   stream_id = 2;
263   index_.ClaimPushedStream(key1_, url1_, HttpRequestInfo(), &session,
264                            &stream_id);
265   EXPECT_EQ(kNoPushedStreamFound, stream_id);
266 
267   // Register two entries.  Second entry uses same key.
268   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 2, &delegate1));
269   TestDelegate delegate2(key1_);
270   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url2_, 4, &delegate2));
271 
272   // Retrieve each entry by their respective URLs.
273   stream_id = kNoPushedStreamFound;
274   index_.ClaimPushedStream(key1_, url1_, HttpRequestInfo(), &session,
275                            &stream_id);
276   EXPECT_EQ(2u, stream_id);
277 
278   stream_id = kNoPushedStreamFound;
279   index_.ClaimPushedStream(key1_, url2_, HttpRequestInfo(), &session,
280                            &stream_id);
281   EXPECT_EQ(4u, stream_id);
282 
283   // ClaimPushedStream() calls unregistered both entries,
284   // cannot claim them again.
285   stream_id = 2;
286   index_.ClaimPushedStream(key1_, url1_, HttpRequestInfo(), &session,
287                            &stream_id);
288   EXPECT_EQ(kNoPushedStreamFound, stream_id);
289 
290   stream_id = 2;
291   index_.ClaimPushedStream(key1_, url2_, HttpRequestInfo(), &session,
292                            &stream_id);
293   EXPECT_EQ(kNoPushedStreamFound, stream_id);
294 
295   EXPECT_FALSE(index_.UnregisterUnclaimedPushedStream(url1_, 2, &delegate1));
296   EXPECT_FALSE(index_.UnregisterUnclaimedPushedStream(url2_, 4, &delegate2));
297 }
298 
299 // Create two entries with delegates that validate different SpdySessionKeys.
300 // Register the two entries with the same URL.  Check that they can be found by
301 // their respective SpdySessionKeys.
TEST_F(Http2PushPromiseIndexTest,MultipleStreamsWithDifferentKeys)302 TEST_F(Http2PushPromiseIndexTest, MultipleStreamsWithDifferentKeys) {
303   // Register first entry.
304   TestDelegate delegate1(key1_);
305   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 2, &delegate1));
306 
307   // No entry found for |key2_|.
308   base::WeakPtr<SpdySession> session;
309   spdy::SpdyStreamId stream_id = 2;
310   index_.ClaimPushedStream(key2_, url1_, HttpRequestInfo(), &session,
311                            &stream_id);
312   EXPECT_EQ(kNoPushedStreamFound, stream_id);
313 
314   // Claim first entry.
315   stream_id = kNoPushedStreamFound;
316   index_.ClaimPushedStream(key1_, url1_, HttpRequestInfo(), &session,
317                            &stream_id);
318   EXPECT_EQ(2u, stream_id);
319 
320   // ClaimPushedStream() unregistered first entry, cannot claim it again.
321   stream_id = 2;
322   index_.ClaimPushedStream(key1_, url1_, HttpRequestInfo(), &session,
323                            &stream_id);
324   EXPECT_EQ(kNoPushedStreamFound, stream_id);
325 
326   // Register two entries.  Second entry uses same URL.
327   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 2, &delegate1));
328   TestDelegate delegate2(key2_);
329   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 4, &delegate2));
330 
331   // Retrieve each entry by their respective SpdySessionKeys.
332   stream_id = kNoPushedStreamFound;
333   index_.ClaimPushedStream(key1_, url1_, HttpRequestInfo(), &session,
334                            &stream_id);
335   EXPECT_EQ(2u, stream_id);
336 
337   stream_id = kNoPushedStreamFound;
338   index_.ClaimPushedStream(key2_, url1_, HttpRequestInfo(), &session,
339                            &stream_id);
340   EXPECT_EQ(4u, stream_id);
341 
342   // ClaimPushedStream() calls unregistered both entries,
343   // cannot claim them again.
344   stream_id = 2;
345   index_.ClaimPushedStream(key1_, url1_, HttpRequestInfo(), &session,
346                            &stream_id);
347   EXPECT_EQ(kNoPushedStreamFound, stream_id);
348 
349   stream_id = 2;
350   index_.ClaimPushedStream(key2_, url1_, HttpRequestInfo(), &session,
351                            &stream_id);
352   EXPECT_EQ(kNoPushedStreamFound, stream_id);
353 
354   EXPECT_FALSE(index_.UnregisterUnclaimedPushedStream(url1_, 2, &delegate1));
355   EXPECT_FALSE(index_.UnregisterUnclaimedPushedStream(url1_, 4, &delegate2));
356 }
357 
TEST_F(Http2PushPromiseIndexTest,MultipleMatchingStreams)358 TEST_F(Http2PushPromiseIndexTest, MultipleMatchingStreams) {
359   // Register two entries with identical URLs that have delegates that accept
360   // the same SpdySessionKey.
361   TestDelegate delegate1(key1_);
362   TestDelegate delegate2(key1_);
363   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 2, &delegate1));
364   EXPECT_TRUE(index_.RegisterUnclaimedPushedStream(url1_, 4, &delegate2));
365 
366   // Test that ClaimPushedStream() returns one of the two entries.
367   // ClaimPushedStream() makes no guarantee about which entry it returns if
368   // there are multiple matches.
369   base::WeakPtr<SpdySession> session;
370   spdy::SpdyStreamId stream_id1 = kNoPushedStreamFound;
371   index_.ClaimPushedStream(key1_, url1_, HttpRequestInfo(), &session,
372                            &stream_id1);
373   EXPECT_NE(kNoPushedStreamFound, stream_id1);
374 
375   // First call to ClaimPushedStream() unregistered one of the entries.
376   // Second call to ClaimPushedStream() must return the other entry.
377   spdy::SpdyStreamId stream_id2 = kNoPushedStreamFound;
378   index_.ClaimPushedStream(key1_, url1_, HttpRequestInfo(), &session,
379                            &stream_id2);
380   EXPECT_NE(kNoPushedStreamFound, stream_id2);
381   EXPECT_NE(stream_id1, stream_id2);
382 
383   // Two calls to ClaimPushedStream() unregistered both entries.
384   spdy::SpdyStreamId stream_id3 = 2;
385   index_.ClaimPushedStream(key1_, url1_, HttpRequestInfo(), &session,
386                            &stream_id3);
387   EXPECT_EQ(kNoPushedStreamFound, stream_id3);
388 
389   EXPECT_FALSE(index_.UnregisterUnclaimedPushedStream(url1_, 2, &delegate1));
390   EXPECT_FALSE(index_.UnregisterUnclaimedPushedStream(url1_, 4, &delegate2));
391 }
392 
393 // Test that an entry is equivalent to itself.
TEST(Http2PushPromiseIndexCompareByUrlTest,Reflexivity)394 TEST(Http2PushPromiseIndexCompareByUrlTest, Reflexivity) {
395   // Test with two entries: with and without a pushed stream.
396   Http2PushPromiseIndexPeer::UnclaimedPushedStream entry1{GURL(), nullptr, 2};
397   Http2PushPromiseIndexPeer::UnclaimedPushedStream entry2{GURL(), nullptr,
398                                                           kNoPushedStreamFound};
399 
400   // For "Compare", it is a requirement that comp(A, A) == false, see
401   // http://en.cppreference.com/w/cpp/concept/Compare.  This will in fact imply
402   // that equiv(A, A) == true.
403   EXPECT_FALSE(Http2PushPromiseIndexPeer::CompareByUrl()(entry1, entry1));
404   EXPECT_FALSE(Http2PushPromiseIndexPeer::CompareByUrl()(entry2, entry2));
405 
406   std::set<Http2PushPromiseIndexPeer::UnclaimedPushedStream,
407            Http2PushPromiseIndexPeer::CompareByUrl>
408       entries;
409   bool success;
410   std::tie(std::ignore, success) = entries.insert(entry1);
411   EXPECT_TRUE(success);
412 
413   // Test that |entry1| is considered equivalent to itself by ensuring that
414   // a second insertion fails.
415   std::tie(std::ignore, success) = entries.insert(entry1);
416   EXPECT_FALSE(success);
417 
418   // Test that |entry1| and |entry2| are not equivalent.
419   std::tie(std::ignore, success) = entries.insert(entry2);
420   EXPECT_TRUE(success);
421 
422   // Test that |entry2| is equivalent to an existing entry
423   // (which then must be |entry2|).
424   std::tie(std::ignore, success) = entries.insert(entry2);
425   EXPECT_FALSE(success);
426 }
427 
TEST(Http2PushPromiseIndexCompareByUrlTest,LookupByURL)428 TEST(Http2PushPromiseIndexCompareByUrlTest, LookupByURL) {
429   const GURL url1("https://example.com:1");
430   const GURL url2("https://example.com:2");
431   const GURL url3("https://example.com:3");
432   // This test relies on the order of these GURLs.
433   ASSERT_LT(url1, url2);
434   ASSERT_LT(url2, url3);
435 
436   // Create four entries, two for the middle URL, with distinct stream IDs not
437   // in ascending order.
438   Http2PushPromiseIndexPeer::UnclaimedPushedStream entry1{url1, nullptr, 8};
439   Http2PushPromiseIndexPeer::UnclaimedPushedStream entry2{url2, nullptr, 4};
440   Http2PushPromiseIndexPeer::UnclaimedPushedStream entry3{url2, nullptr, 6};
441   Http2PushPromiseIndexPeer::UnclaimedPushedStream entry4{url3, nullptr, 2};
442 
443   // Fill up a set.
444   std::set<Http2PushPromiseIndexPeer::UnclaimedPushedStream,
445            Http2PushPromiseIndexPeer::CompareByUrl>
446       entries;
447   entries.insert(entry1);
448   entries.insert(entry2);
449   entries.insert(entry3);
450   entries.insert(entry4);
451   ASSERT_EQ(4u, entries.size());
452 
453   // Test that entries are ordered by URL first, not stream ID.
454   auto it = entries.begin();
455   EXPECT_EQ(8u, it->stream_id);
456   ++it;
457   EXPECT_EQ(4u, it->stream_id);
458   ++it;
459   EXPECT_EQ(6u, it->stream_id);
460   ++it;
461   EXPECT_EQ(2u, it->stream_id);
462   ++it;
463   EXPECT_TRUE(it == entries.end());
464 
465   // Test that kNoPushedStreamFound can be used to look up the first entry for a
466   // given URL.  In particular, the first entry with |url2| is |entry2|.
467   EXPECT_TRUE(
468       entries.lower_bound(Http2PushPromiseIndexPeer::UnclaimedPushedStream{
469           url2, nullptr, kNoPushedStreamFound}) == entries.find(entry2));
470 }
471 
472 }  // namespace net::test
473