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