• 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 <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