Add UMA reporting to CdmPromise.
[chromium-blink-merge.git] / net / quic / quic_time_wait_list_manager.cc
blobd1f3419e77dbf79ee09284c834c890a7a30a1429
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/quic/quic_time_wait_list_manager.h"
7 #include <errno.h>
9 #include "base/containers/hash_tables.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/stl_util.h"
12 #include "net/base/ip_endpoint.h"
13 #include "net/quic/crypto/crypto_protocol.h"
14 #include "net/quic/crypto/quic_decrypter.h"
15 #include "net/quic/crypto/quic_encrypter.h"
16 #include "net/quic/quic_clock.h"
17 #include "net/quic/quic_connection_helper.h"
18 #include "net/quic/quic_framer.h"
19 #include "net/quic/quic_protocol.h"
20 #include "net/quic/quic_server_session.h"
21 #include "net/quic/quic_utils.h"
23 using base::StringPiece;
24 using std::make_pair;
26 namespace net {
28 namespace {
30 // Time period for which the connection_id should live in time wait state..
31 const int kTimeWaitSeconds = 5;
33 } // namespace
35 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
36 // up old connection_ids. This alarm should be unregistered and deleted before
37 // the QuicTimeWaitListManager is deleted.
38 class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate {
39 public:
40 explicit ConnectionIdCleanUpAlarm(
41 QuicTimeWaitListManager* time_wait_list_manager)
42 : time_wait_list_manager_(time_wait_list_manager) {}
44 virtual QuicTime OnAlarm() OVERRIDE {
45 time_wait_list_manager_->CleanUpOldConnectionIds();
46 // Let the time wait manager register the alarm at appropriate time.
47 return QuicTime::Zero();
50 private:
51 // Not owned.
52 QuicTimeWaitListManager* time_wait_list_manager_;
55 // This class stores pending public reset packets to be sent to clients.
56 // server_address - server address on which a packet what was received for
57 // a connection_id in time wait state.
58 // client_address - address of the client that sent that packet. Needed to send
59 // the public reset packet back to the client.
60 // packet - the pending public reset packet that is to be sent to the client.
61 // created instance takes the ownership of this packet.
62 class QuicTimeWaitListManager::QueuedPacket {
63 public:
64 QueuedPacket(const IPEndPoint& server_address,
65 const IPEndPoint& client_address,
66 QuicEncryptedPacket* packet)
67 : server_address_(server_address),
68 client_address_(client_address),
69 packet_(packet) {}
71 const IPEndPoint& server_address() const { return server_address_; }
72 const IPEndPoint& client_address() const { return client_address_; }
73 QuicEncryptedPacket* packet() { return packet_.get(); }
75 private:
76 const IPEndPoint server_address_;
77 const IPEndPoint client_address_;
78 scoped_ptr<QuicEncryptedPacket> packet_;
80 DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
83 QuicTimeWaitListManager::QuicTimeWaitListManager(
84 QuicPacketWriter* writer,
85 QuicServerSessionVisitor* visitor,
86 QuicConnectionHelperInterface* helper,
87 const QuicVersionVector& supported_versions)
88 : helper_(helper),
89 kTimeWaitPeriod_(QuicTime::Delta::FromSeconds(kTimeWaitSeconds)),
90 connection_id_clean_up_alarm_(
91 helper_->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
92 writer_(writer),
93 visitor_(visitor) {
94 SetConnectionIdCleanUpAlarm();
97 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
98 connection_id_clean_up_alarm_->Cancel();
99 STLDeleteElements(&pending_packets_queue_);
100 for (ConnectionIdMap::iterator it = connection_id_map_.begin();
101 it != connection_id_map_.end();
102 ++it) {
103 delete it->second.close_packet;
107 void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
108 QuicConnectionId connection_id,
109 QuicVersion version,
110 QuicEncryptedPacket* close_packet) {
111 DVLOG(1) << "Adding " << connection_id << " to the time wait list.";
112 int num_packets = 0;
113 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
114 if (it != connection_id_map_.end()) { // Replace record if it is reinserted.
115 num_packets = it->second.num_packets;
116 delete it->second.close_packet;
117 connection_id_map_.erase(it);
119 ConnectionIdData data(num_packets,
120 version,
121 helper_->GetClock()->ApproximateNow(),
122 close_packet);
123 connection_id_map_.insert(make_pair(connection_id, data));
126 bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
127 QuicConnectionId connection_id) const {
128 return ContainsKey(connection_id_map_, connection_id);
131 QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
132 QuicConnectionId connection_id) {
133 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
134 DCHECK(it != connection_id_map_.end());
135 return (it->second).version;
138 void QuicTimeWaitListManager::OnCanWrite() {
139 while (!pending_packets_queue_.empty()) {
140 QueuedPacket* queued_packet = pending_packets_queue_.front();
141 if (!WriteToWire(queued_packet)) {
142 return;
144 pending_packets_queue_.pop_front();
145 delete queued_packet;
149 void QuicTimeWaitListManager::ProcessPacket(
150 const IPEndPoint& server_address,
151 const IPEndPoint& client_address,
152 QuicConnectionId connection_id,
153 QuicPacketSequenceNumber sequence_number,
154 const QuicEncryptedPacket& /*packet*/) {
155 DCHECK(IsConnectionIdInTimeWait(connection_id));
156 DVLOG(1) << "Processing " << connection_id << " in time wait state.";
157 // TODO(satyamshekhar): Think about handling packets from different client
158 // addresses.
159 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
160 DCHECK(it != connection_id_map_.end());
161 // Increment the received packet count.
162 ++((it->second).num_packets);
163 if (!ShouldSendResponse((it->second).num_packets)) {
164 return;
166 if (it->second.close_packet) {
167 QueuedPacket* queued_packet =
168 new QueuedPacket(server_address,
169 client_address,
170 it->second.close_packet->Clone());
171 // Takes ownership of the packet.
172 SendOrQueuePacket(queued_packet);
173 } else {
174 SendPublicReset(server_address,
175 client_address,
176 connection_id,
177 sequence_number);
181 // Returns true if the number of packets received for this connection_id is a
182 // power of 2 to throttle the number of public reset packets we send to a
183 // client.
184 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
185 return (received_packet_count & (received_packet_count - 1)) == 0;
188 void QuicTimeWaitListManager::SendPublicReset(
189 const IPEndPoint& server_address,
190 const IPEndPoint& client_address,
191 QuicConnectionId connection_id,
192 QuicPacketSequenceNumber rejected_sequence_number) {
193 QuicPublicResetPacket packet;
194 packet.public_header.connection_id = connection_id;
195 packet.public_header.reset_flag = true;
196 packet.public_header.version_flag = false;
197 packet.rejected_sequence_number = rejected_sequence_number;
198 // TODO(satyamshekhar): generate a valid nonce for this connection_id.
199 packet.nonce_proof = 1010101;
200 packet.client_address = client_address;
201 QueuedPacket* queued_packet = new QueuedPacket(
202 server_address,
203 client_address,
204 BuildPublicReset(packet));
205 // Takes ownership of the packet.
206 SendOrQueuePacket(queued_packet);
209 QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset(
210 const QuicPublicResetPacket& packet) {
211 return QuicFramer::BuildPublicResetPacket(packet);
214 // Either sends the packet and deletes it or makes pending queue the
215 // owner of the packet.
216 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
217 if (WriteToWire(packet)) {
218 delete packet;
219 } else {
220 // pending_packets_queue takes the ownership of the queued packet.
221 pending_packets_queue_.push_back(packet);
225 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
226 if (writer_->IsWriteBlocked()) {
227 visitor_->OnWriteBlocked(this);
228 return false;
230 WriteResult result = writer_->WritePacket(
231 queued_packet->packet()->data(),
232 queued_packet->packet()->length(),
233 queued_packet->server_address().address(),
234 queued_packet->client_address());
235 if (result.status == WRITE_STATUS_BLOCKED) {
236 // If blocked and unbuffered, return false to retry sending.
237 DCHECK(writer_->IsWriteBlocked());
238 visitor_->OnWriteBlocked(this);
239 return writer_->IsWriteBlockedDataBuffered();
240 } else if (result.status == WRITE_STATUS_ERROR) {
241 LOG(WARNING) << "Received unknown error while sending reset packet to "
242 << queued_packet->client_address().ToString() << ": "
243 << strerror(result.error_code);
245 return true;
248 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
249 connection_id_clean_up_alarm_->Cancel();
250 QuicTime now = helper_->GetClock()->ApproximateNow();
251 QuicTime next_alarm_time = now;
252 if (!connection_id_map_.empty()) {
253 QuicTime oldest_connection_id =
254 connection_id_map_.begin()->second.time_added;
255 if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
256 next_alarm_time = oldest_connection_id.Add(kTimeWaitPeriod_);
257 } else {
258 LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod";
260 } else {
261 // No connection_ids added so none will expire before kTimeWaitPeriod_.
262 next_alarm_time = now.Add(kTimeWaitPeriod_);
265 connection_id_clean_up_alarm_->Set(next_alarm_time);
268 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
269 QuicTime now = helper_->GetClock()->ApproximateNow();
270 while (!connection_id_map_.empty()) {
271 ConnectionIdMap::iterator it = connection_id_map_.begin();
272 QuicTime oldest_connection_id = it->second.time_added;
273 if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
274 break;
276 // This connection_id has lived its age, retire it now.
277 delete it->second.close_packet;
278 connection_id_map_.erase(it);
280 SetConnectionIdCleanUpAlarm();
283 } // namespace net