| // Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/spdy/http2_push_promise_index.h" |
| |
| #include <algorithm> |
| #include <utility> |
| |
| #include "base/trace_event/memory_usage_estimator.h" |
| |
| namespace net { |
| |
| Http2PushPromiseIndex::Http2PushPromiseIndex() = default; |
| |
| Http2PushPromiseIndex::~Http2PushPromiseIndex() { |
| DCHECK(unclaimed_pushed_streams_.empty()); |
| } |
| |
| bool Http2PushPromiseIndex::RegisterUnclaimedPushedStream( |
| const GURL& url, |
| spdy::SpdyStreamId stream_id, |
| Delegate* delegate) { |
| DCHECK(!url.is_empty()); |
| DCHECK_GT(stream_id, kNoPushedStreamFound); |
| DCHECK(delegate); |
| |
| // Find the entry with |url| for |delegate| if such exists (there can be at |
| // most one such entry). It is okay to cast away const from |delegate|, |
| // because it is only used for lookup. |
| auto it = unclaimed_pushed_streams_.lower_bound(UnclaimedPushedStream{ |
| url, const_cast<Delegate*>(delegate), kNoPushedStreamFound}); |
| // If such entry is found, do not allow registering another one. |
| if (it != unclaimed_pushed_streams_.end() && it->url == url && |
| it->delegate == delegate) { |
| return false; |
| } |
| |
| unclaimed_pushed_streams_.insert( |
| it, UnclaimedPushedStream{url, delegate, stream_id}); |
| |
| return true; |
| } |
| |
| bool Http2PushPromiseIndex::UnregisterUnclaimedPushedStream( |
| const GURL& url, |
| spdy::SpdyStreamId stream_id, |
| Delegate* delegate) { |
| DCHECK(!url.is_empty()); |
| DCHECK_GT(stream_id, kNoPushedStreamFound); |
| DCHECK(delegate); |
| |
| size_t result = unclaimed_pushed_streams_.erase( |
| UnclaimedPushedStream{url, delegate, stream_id}); |
| |
| return result == 1; |
| } |
| |
| // The runtime of this method is linear in unclaimed_pushed_streams_.size(), |
| // which is acceptable, because it is only used in NetLog, tests, and DCHECKs. |
| size_t Http2PushPromiseIndex::CountStreamsForSession( |
| const Delegate* delegate) const { |
| DCHECK(delegate); |
| |
| return std::count_if(unclaimed_pushed_streams_.begin(), |
| unclaimed_pushed_streams_.end(), |
| [&delegate](const UnclaimedPushedStream& entry) { |
| return entry.delegate == delegate; |
| }); |
| } |
| |
| spdy::SpdyStreamId Http2PushPromiseIndex::FindStream( |
| const GURL& url, |
| const Delegate* delegate) const { |
| // Find the entry with |url| for |delegate| if such exists (there can be at |
| // most one such entry). It is okay to cast away const from |delegate|, |
| // because it is only used for lookup. |
| auto it = unclaimed_pushed_streams_.lower_bound(UnclaimedPushedStream{ |
| url, const_cast<Delegate*>(delegate), kNoPushedStreamFound}); |
| |
| if (it == unclaimed_pushed_streams_.end() || it->url != url || |
| it->delegate != delegate) { |
| return kNoPushedStreamFound; |
| } |
| |
| return it->stream_id; |
| } |
| |
| void Http2PushPromiseIndex::ClaimPushedStream( |
| const SpdySessionKey& key, |
| const GURL& url, |
| const HttpRequestInfo& request_info, |
| base::WeakPtr<SpdySession>* session, |
| spdy::SpdyStreamId* stream_id) { |
| DCHECK(!url.is_empty()); |
| |
| *session = nullptr; |
| *stream_id = kNoPushedStreamFound; |
| |
| // Find the first entry for |url|, if such exists. |
| auto it = unclaimed_pushed_streams_.lower_bound( |
| UnclaimedPushedStream{url, nullptr, kNoPushedStreamFound}); |
| |
| while (it != unclaimed_pushed_streams_.end() && it->url == url) { |
| if (it->delegate->ValidatePushedStream(it->stream_id, url, request_info, |
| key)) { |
| *session = it->delegate->GetWeakPtrToSession(); |
| *stream_id = it->stream_id; |
| unclaimed_pushed_streams_.erase(it); |
| return; |
| } |
| ++it; |
| } |
| } |
| |
| size_t Http2PushPromiseIndex::EstimateMemoryUsage() const { |
| return base::trace_event::EstimateMemoryUsage(unclaimed_pushed_streams_); |
| } |
| |
| size_t Http2PushPromiseIndex::UnclaimedPushedStream::EstimateMemoryUsage() |
| const { |
| return base::trace_event::EstimateMemoryUsage(url) + |
| sizeof(spdy::SpdyStreamId) + sizeof(Delegate*); |
| } |
| |
| bool Http2PushPromiseIndex::CompareByUrl::operator()( |
| const UnclaimedPushedStream& a, |
| const UnclaimedPushedStream& b) const { |
| // Compare by URL first. |
| if (a.url < b.url) |
| return true; |
| if (a.url > b.url) |
| return false; |
| // For identical URL, put an entry with delegate == nullptr first. |
| // The C++ standard dictates that comparisons between |nullptr| and other |
| // pointers are unspecified, hence the need to handle this case separately. |
| if (a.delegate == nullptr && b.delegate != nullptr) { |
| return true; |
| } |
| if (a.delegate != nullptr && b.delegate == nullptr) { |
| return false; |
| } |
| // Then compare by Delegate. |
| // The C++ standard guarantees that both |nullptr < nullptr| and |
| // |nullptr > nullptr| are false, so there is no need to handle that case |
| // separately. |
| if (a.delegate < b.delegate) |
| return true; |
| if (a.delegate > b.delegate) |
| return false; |
| // If URL and Delegate are identical, then compare by stream ID. |
| return a.stream_id < b.stream_id; |
| } |
| |
| } // namespace net |