summaryrefslogtreecommitdiff
path: root/chromium/net/spdy/http2_push_promise_index.cc
blob: e3e2e92c512736748cb3375f5e3dd739265aee44 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Copyright 2017 The Chromium Authors
// 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;
  }
}

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