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 <utility>
8
9 #include "base/ranges/algorithm.h"
10 #include "base/trace_event/memory_usage_estimator.h"
11
12 namespace net {
13
14 Http2PushPromiseIndex::Http2PushPromiseIndex() = default;
15
~Http2PushPromiseIndex()16 Http2PushPromiseIndex::~Http2PushPromiseIndex() {
17 DCHECK(unclaimed_pushed_streams_.empty());
18 }
19
RegisterUnclaimedPushedStream(const GURL & url,spdy::SpdyStreamId stream_id,Delegate * delegate)20 bool Http2PushPromiseIndex::RegisterUnclaimedPushedStream(
21 const GURL& url,
22 spdy::SpdyStreamId stream_id,
23 Delegate* delegate) {
24 DCHECK(!url.is_empty());
25 DCHECK_GT(stream_id, kNoPushedStreamFound);
26 DCHECK(delegate);
27
28 // Find the entry with |url| for |delegate| if such exists (there can be at
29 // most one such entry). It is okay to cast away const from |delegate|,
30 // because it is only used for lookup.
31 auto it = unclaimed_pushed_streams_.lower_bound(UnclaimedPushedStream{
32 url, const_cast<Delegate*>(delegate), kNoPushedStreamFound});
33 // If such entry is found, do not allow registering another one.
34 if (it != unclaimed_pushed_streams_.end() && it->url == url &&
35 it->delegate == delegate) {
36 return false;
37 }
38
39 unclaimed_pushed_streams_.insert(
40 it, UnclaimedPushedStream{url, delegate, stream_id});
41
42 return true;
43 }
44
UnregisterUnclaimedPushedStream(const GURL & url,spdy::SpdyStreamId stream_id,Delegate * delegate)45 bool Http2PushPromiseIndex::UnregisterUnclaimedPushedStream(
46 const GURL& url,
47 spdy::SpdyStreamId stream_id,
48 Delegate* delegate) {
49 DCHECK(!url.is_empty());
50 DCHECK_GT(stream_id, kNoPushedStreamFound);
51 DCHECK(delegate);
52
53 size_t result = unclaimed_pushed_streams_.erase(
54 UnclaimedPushedStream{url, delegate, stream_id});
55
56 return result == 1;
57 }
58
59 // The runtime of this method is linear in unclaimed_pushed_streams_.size(),
60 // which is acceptable, because it is only used in NetLog, tests, and DCHECKs.
CountStreamsForSession(const Delegate * delegate) const61 size_t Http2PushPromiseIndex::CountStreamsForSession(
62 const Delegate* delegate) const {
63 DCHECK(delegate);
64
65 return base::ranges::count(unclaimed_pushed_streams_, delegate,
66 &UnclaimedPushedStream::delegate);
67 }
68
FindStream(const GURL & url,const Delegate * delegate) const69 spdy::SpdyStreamId Http2PushPromiseIndex::FindStream(
70 const GURL& url,
71 const Delegate* delegate) const {
72 // Find the entry with |url| for |delegate| if such exists (there can be at
73 // most one such entry). It is okay to cast away const from |delegate|,
74 // because it is only used for lookup.
75 auto it = unclaimed_pushed_streams_.lower_bound(UnclaimedPushedStream{
76 url, const_cast<Delegate*>(delegate), kNoPushedStreamFound});
77
78 if (it == unclaimed_pushed_streams_.end() || it->url != url ||
79 it->delegate != delegate) {
80 return kNoPushedStreamFound;
81 }
82
83 return it->stream_id;
84 }
85
ClaimPushedStream(const SpdySessionKey & key,const GURL & url,const HttpRequestInfo & request_info,base::WeakPtr<SpdySession> * session,spdy::SpdyStreamId * stream_id)86 void Http2PushPromiseIndex::ClaimPushedStream(
87 const SpdySessionKey& key,
88 const GURL& url,
89 const HttpRequestInfo& request_info,
90 base::WeakPtr<SpdySession>* session,
91 spdy::SpdyStreamId* stream_id) {
92 DCHECK(!url.is_empty());
93
94 *session = nullptr;
95 *stream_id = kNoPushedStreamFound;
96
97 // Find the first entry for |url|, if such exists.
98 auto it = unclaimed_pushed_streams_.lower_bound(
99 UnclaimedPushedStream{url, nullptr, kNoPushedStreamFound});
100
101 while (it != unclaimed_pushed_streams_.end() && it->url == url) {
102 if (it->delegate->ValidatePushedStream(it->stream_id, url, request_info,
103 key)) {
104 *session = it->delegate->GetWeakPtrToSession();
105 *stream_id = it->stream_id;
106 unclaimed_pushed_streams_.erase(it);
107 return;
108 }
109 ++it;
110 }
111 }
112
operator ()(const UnclaimedPushedStream & a,const UnclaimedPushedStream & b) const113 bool Http2PushPromiseIndex::CompareByUrl::operator()(
114 const UnclaimedPushedStream& a,
115 const UnclaimedPushedStream& b) const {
116 // Compare by URL first.
117 if (a.url < b.url)
118 return true;
119 if (a.url > b.url)
120 return false;
121 // For identical URL, put an entry with delegate == nullptr first.
122 // The C++ standard dictates that comparisons between |nullptr| and other
123 // pointers are unspecified, hence the need to handle this case separately.
124 if (a.delegate == nullptr && b.delegate != nullptr) {
125 return true;
126 }
127 if (a.delegate != nullptr && b.delegate == nullptr) {
128 return false;
129 }
130 // Then compare by Delegate.
131 // The C++ standard guarantees that both |nullptr < nullptr| and
132 // |nullptr > nullptr| are false, so there is no need to handle that case
133 // separately.
134 if (a.delegate < b.delegate)
135 return true;
136 if (a.delegate > b.delegate)
137 return false;
138 // If URL and Delegate are identical, then compare by stream ID.
139 return a.stream_id < b.stream_id;
140 }
141
142 } // namespace net
143