Roll src/third_party/skia 5cc0f6c:01d3319
[chromium-blink-merge.git] / net / quic / quic_time_wait_list_manager.cc
blob52ee2da0534acf54b2cf8ff27b2992f7c7641406
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_flags.h"
19 #include "net/quic/quic_framer.h"
20 #include "net/quic/quic_protocol.h"
21 #include "net/quic/quic_server_session.h"
22 #include "net/quic/quic_utils.h"
24 using base::StringPiece;
25 using std::make_pair;
27 namespace net {
29 // A very simple alarm that just informs the QuicTimeWaitListManager to clean
30 // up old connection_ids. This alarm should be unregistered and deleted before
31 // the QuicTimeWaitListManager is deleted.
32 class ConnectionIdCleanUpAlarm : public QuicAlarm::Delegate {
33 public:
34 explicit ConnectionIdCleanUpAlarm(
35 QuicTimeWaitListManager* time_wait_list_manager)
36 : time_wait_list_manager_(time_wait_list_manager) {
39 QuicTime OnAlarm() override {
40 time_wait_list_manager_->CleanUpOldConnectionIds();
41 // Let the time wait manager register the alarm at appropriate time.
42 return QuicTime::Zero();
45 private:
46 // Not owned.
47 QuicTimeWaitListManager* time_wait_list_manager_;
50 // This class stores pending public reset packets to be sent to clients.
51 // server_address - server address on which a packet what was received for
52 // a connection_id in time wait state.
53 // client_address - address of the client that sent that packet. Needed to send
54 // the public reset packet back to the client.
55 // packet - the pending public reset packet that is to be sent to the client.
56 // created instance takes the ownership of this packet.
57 class QuicTimeWaitListManager::QueuedPacket {
58 public:
59 QueuedPacket(const IPEndPoint& server_address,
60 const IPEndPoint& client_address,
61 QuicEncryptedPacket* packet)
62 : server_address_(server_address),
63 client_address_(client_address),
64 packet_(packet) {
67 const IPEndPoint& server_address() const { return server_address_; }
68 const IPEndPoint& client_address() const { return client_address_; }
69 QuicEncryptedPacket* packet() { return packet_.get(); }
71 private:
72 const IPEndPoint server_address_;
73 const IPEndPoint client_address_;
74 scoped_ptr<QuicEncryptedPacket> packet_;
76 DISALLOW_COPY_AND_ASSIGN(QueuedPacket);
79 QuicTimeWaitListManager::QuicTimeWaitListManager(
80 QuicPacketWriter* writer,
81 QuicServerSessionVisitor* visitor,
82 QuicConnectionHelperInterface* helper,
83 const QuicVersionVector& supported_versions)
84 : helper_(helper),
85 kTimeWaitPeriod_(
86 QuicTime::Delta::FromSeconds(FLAGS_quic_time_wait_list_seconds)),
87 connection_id_clean_up_alarm_(
88 helper_->CreateAlarm(new ConnectionIdCleanUpAlarm(this))),
89 writer_(writer),
90 visitor_(visitor) {
91 SetConnectionIdCleanUpAlarm();
94 QuicTimeWaitListManager::~QuicTimeWaitListManager() {
95 connection_id_clean_up_alarm_->Cancel();
96 STLDeleteElements(&pending_packets_queue_);
97 for (ConnectionIdMap::iterator it = connection_id_map_.begin();
98 it != connection_id_map_.end();
99 ++it) {
100 delete it->second.close_packet;
104 void QuicTimeWaitListManager::AddConnectionIdToTimeWait(
105 QuicConnectionId connection_id,
106 QuicVersion version,
107 QuicEncryptedPacket* close_packet) {
108 int num_packets = 0;
109 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
110 const bool new_connection_id = it == connection_id_map_.end();
111 if (!new_connection_id) { // Replace record if it is reinserted.
112 num_packets = it->second.num_packets;
113 delete it->second.close_packet;
114 connection_id_map_.erase(it);
116 TrimTimeWaitListIfNeeded();
117 if (FLAGS_quic_limit_time_wait_list_size) {
118 DCHECK_LT(num_connections(),
119 static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections));
121 ConnectionIdData data(num_packets,
122 version,
123 helper_->GetClock()->ApproximateNow(),
124 close_packet);
125 connection_id_map_.insert(make_pair(connection_id, data));
126 if (new_connection_id) {
127 visitor_->OnConnectionAddedToTimeWaitList(connection_id);
131 bool QuicTimeWaitListManager::IsConnectionIdInTimeWait(
132 QuicConnectionId connection_id) const {
133 return ContainsKey(connection_id_map_, connection_id);
136 QuicVersion QuicTimeWaitListManager::GetQuicVersionFromConnectionId(
137 QuicConnectionId connection_id) {
138 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
139 DCHECK(it != connection_id_map_.end());
140 return (it->second).version;
143 void QuicTimeWaitListManager::OnCanWrite() {
144 while (!pending_packets_queue_.empty()) {
145 QueuedPacket* queued_packet = pending_packets_queue_.front();
146 if (!WriteToWire(queued_packet)) {
147 return;
149 pending_packets_queue_.pop_front();
150 delete queued_packet;
154 void QuicTimeWaitListManager::ProcessPacket(
155 const IPEndPoint& server_address,
156 const IPEndPoint& client_address,
157 QuicConnectionId connection_id,
158 QuicPacketSequenceNumber sequence_number,
159 const QuicEncryptedPacket& /*packet*/) {
160 DCHECK(IsConnectionIdInTimeWait(connection_id));
161 DVLOG(1) << "Processing " << connection_id << " in time wait state.";
162 // TODO(satyamshekhar): Think about handling packets from different client
163 // addresses.
164 ConnectionIdMap::iterator it = connection_id_map_.find(connection_id);
165 DCHECK(it != connection_id_map_.end());
166 // Increment the received packet count.
167 ++((it->second).num_packets);
168 if (!ShouldSendResponse((it->second).num_packets)) {
169 return;
171 if (it->second.close_packet) {
172 QueuedPacket* queued_packet =
173 new QueuedPacket(server_address,
174 client_address,
175 it->second.close_packet->Clone());
176 // Takes ownership of the packet.
177 SendOrQueuePacket(queued_packet);
178 } else {
179 SendPublicReset(server_address,
180 client_address,
181 connection_id,
182 sequence_number);
186 // Returns true if the number of packets received for this connection_id is a
187 // power of 2 to throttle the number of public reset packets we send to a
188 // client.
189 bool QuicTimeWaitListManager::ShouldSendResponse(int received_packet_count) {
190 return (received_packet_count & (received_packet_count - 1)) == 0;
193 void QuicTimeWaitListManager::SendPublicReset(
194 const IPEndPoint& server_address,
195 const IPEndPoint& client_address,
196 QuicConnectionId connection_id,
197 QuicPacketSequenceNumber rejected_sequence_number) {
198 QuicPublicResetPacket packet;
199 packet.public_header.connection_id = connection_id;
200 packet.public_header.reset_flag = true;
201 packet.public_header.version_flag = false;
202 packet.rejected_sequence_number = rejected_sequence_number;
203 // TODO(satyamshekhar): generate a valid nonce for this connection_id.
204 packet.nonce_proof = 1010101;
205 packet.client_address = client_address;
206 QueuedPacket* queued_packet = new QueuedPacket(
207 server_address,
208 client_address,
209 BuildPublicReset(packet));
210 // Takes ownership of the packet.
211 SendOrQueuePacket(queued_packet);
214 QuicEncryptedPacket* QuicTimeWaitListManager::BuildPublicReset(
215 const QuicPublicResetPacket& packet) {
216 return QuicFramer::BuildPublicResetPacket(packet);
219 // Either sends the packet and deletes it or makes pending queue the
220 // owner of the packet.
221 void QuicTimeWaitListManager::SendOrQueuePacket(QueuedPacket* packet) {
222 if (WriteToWire(packet)) {
223 delete packet;
224 } else {
225 // pending_packets_queue takes the ownership of the queued packet.
226 pending_packets_queue_.push_back(packet);
230 bool QuicTimeWaitListManager::WriteToWire(QueuedPacket* queued_packet) {
231 if (writer_->IsWriteBlocked()) {
232 visitor_->OnWriteBlocked(this);
233 return false;
235 WriteResult result = writer_->WritePacket(
236 queued_packet->packet()->data(),
237 queued_packet->packet()->length(),
238 queued_packet->server_address().address(),
239 queued_packet->client_address());
240 if (result.status == WRITE_STATUS_BLOCKED) {
241 // If blocked and unbuffered, return false to retry sending.
242 DCHECK(writer_->IsWriteBlocked());
243 visitor_->OnWriteBlocked(this);
244 return writer_->IsWriteBlockedDataBuffered();
245 } else if (result.status == WRITE_STATUS_ERROR) {
246 LOG(WARNING) << "Received unknown error while sending reset packet to "
247 << queued_packet->client_address().ToString() << ": "
248 << strerror(result.error_code);
250 return true;
253 void QuicTimeWaitListManager::SetConnectionIdCleanUpAlarm() {
254 connection_id_clean_up_alarm_->Cancel();
255 QuicTime now = helper_->GetClock()->ApproximateNow();
256 QuicTime next_alarm_time = now;
257 if (!connection_id_map_.empty()) {
258 QuicTime oldest_connection_id =
259 connection_id_map_.begin()->second.time_added;
260 if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
261 next_alarm_time = oldest_connection_id.Add(kTimeWaitPeriod_);
262 } else {
263 LOG(ERROR) << "ConnectionId lingered for longer than kTimeWaitPeriod";
265 } else {
266 // No connection_ids added so none will expire before kTimeWaitPeriod_.
267 next_alarm_time = now.Add(kTimeWaitPeriod_);
270 connection_id_clean_up_alarm_->Set(next_alarm_time);
273 bool QuicTimeWaitListManager::MaybeExpireOldestConnection(
274 QuicTime expiration_time) {
275 if (connection_id_map_.empty()) {
276 return false;
278 ConnectionIdMap::iterator it = connection_id_map_.begin();
279 QuicTime oldest_connection_id_time = it->second.time_added;
280 if (oldest_connection_id_time > expiration_time) {
281 // Too recent, don't retire.
282 return false;
284 // This connection_id has lived its age, retire it now.
285 const QuicConnectionId connection_id = it->first;
286 delete it->second.close_packet;
287 connection_id_map_.erase(it);
288 visitor_->OnConnectionRemovedFromTimeWaitList(connection_id);
289 return true;
292 void QuicTimeWaitListManager::CleanUpOldConnectionIds() {
293 QuicTime now = helper_->GetClock()->ApproximateNow();
294 QuicTime expiration = now.Subtract(kTimeWaitPeriod_);
295 if (FLAGS_quic_limit_time_wait_list_size) {
296 while (MaybeExpireOldestConnection(expiration)) {
298 } else {
299 while (!connection_id_map_.empty()) {
300 ConnectionIdMap::iterator it = connection_id_map_.begin();
301 QuicTime oldest_connection_id = it->second.time_added;
302 if (now.Subtract(oldest_connection_id) < kTimeWaitPeriod_) {
303 break;
305 const QuicConnectionId connection_id = it->first;
306 // This connection_id has lived its age, retire it now.
307 delete it->second.close_packet;
308 connection_id_map_.erase(it);
309 visitor_->OnConnectionRemovedFromTimeWaitList(connection_id);
313 SetConnectionIdCleanUpAlarm();
316 void QuicTimeWaitListManager::TrimTimeWaitListIfNeeded() {
317 if (FLAGS_quic_limit_time_wait_list_size) {
318 if (FLAGS_quic_time_wait_list_max_connections < 0) {
319 return;
321 while (num_connections() >=
322 static_cast<size_t>(FLAGS_quic_time_wait_list_max_connections)) {
323 MaybeExpireOldestConnection(QuicTime::Infinite());
328 } // namespace net